本系列主要翻譯自《ASP.NET MVC Interview Questions and Answers 》- By Shailendra Chauhan,想看英文原版的可訪問http://www.dotnettricks.com/free-ebooks自行下載。該書主要分為兩部分,ASP.NET MVC 5、ASP.NET WEB API2。本書最大的特點是以面試問答的形式進行展開。通讀此書,會幫助你對ASP.NET MVC有更深層次的理解。
由于個人技術水平和英文水平也是有限的,因此錯誤在所難免,希望大家多多留言指正。
系列導航
Asp.net mvc 知多少(一)
Asp.net mvc 知多少(二)
Asp.net mvc 知多少(三)
Asp.net mvc 知多少(四)
Asp.net mvc 知多少(五)
Asp.net mvc 知多少(六)
Asp.net mvc 知多少(七)
Asp.net mvc 知多少(八)
Asp.net mvc 知多少(九)
Asp.net mvc 知多少(十)
本節(jié)主要講解MVC的管道及路由機制
Q13. Asp.net mvc 中的ViewModel?
Ans. 在 ASP.NET MVC中, ViewModel 是一個包含將在強類型視圖中展示的字段的類。它是用來將數(shù)據(jù)從Controller傳遞到強類型視圖中。
ViewModel的關鍵點:
- ViewModel 包含在視圖中呈現(xiàn)的字段。(LabelFor, EditorFor, DisplayFor helpers)
- ViewModel 可以通過數(shù)據(jù)注解指定特定的驗證規(guī)則。
- ViewModel 可以包含多個來自不同數(shù)據(jù)模型或數(shù)據(jù)源的實體或對象。
Q14. 解釋下 ASP.NET MVC pipeline(管道)?
Ans. 先上圖:

Routing(路由) - 路由是管道的第一步。簡單來說,它是一種模式匹配系統(tǒng),去路由表中注冊的Url中匹配傳入的請求。在代碼中主要是
UrlRoutingModule(System.Web.Routing.UrlRoutingModule)在做匹配的工作,路由表對應的是RouteTable(System.Web.Routing.RouteTable)。Controller Initialization(初始化控制器) - MvcHandler使用ProcessRequest方法開始對ASP.NET MVC pipeline進行實時處理。這個方法使用工廠類
IControllerFactory的實例(默認是System.Web.Mvc.DefaultControllerFactory)去創(chuàng)建對應的Controller。Action Execution (Action執(zhí)行)– 該環(huán)節(jié)按以下順序執(zhí)行:
當Controller初始化后,Controller通過傳遞選擇的action方法詳情調用它自己的
InvokeAction()方法。這一步是由IActionInvoker處理。當選擇合適的action方法后,model binder(模型綁定器,默認是
System.Web.Mvc.DefaultModelBinder)取回傳入的Http請求的數(shù)據(jù),然后進行數(shù)據(jù)轉換,數(shù)據(jù)驗證(比如required、數(shù)據(jù)格式等)。同時還需要將數(shù)據(jù)映射到action方法對應的參數(shù)上。Authentication Filter (認證過濾器)是在ASP.NET MVC5中引入的,它先于authorization filter(授權過濾器)執(zhí)行。它主要用來對用戶認證。認證過濾器處理請求中的用戶憑證并返回相應的主體。在ASP.NET MVC5之前,使用 authorization filter (授權過濾器)對用戶進行認證和授權。 Authenticate attribute(認證特性)默認是被用來進行認證. 可以通過實現(xiàn)
IAuthenticationFilter接口來創(chuàng)建自定義的authentication filter(認證過濾器)Authorization filter(授權過濾器)用來對已認證的用戶執(zhí)行授權操作。例如?;诮巧氖跈唷uthorize attribute(授權特性默認用來執(zhí)行授權操作)。可以通過實現(xiàn)
IAuthorizationFilter接口來創(chuàng)建自定義的authentication filter(授權過濾器)。Action filters (Action過濾器)在
OnActionExecuting之前OnActionExecuting之后執(zhí)行。IActionFilter接口提供了兩個方法OnActionExecuting、OnActionExecuting分別在action之前和之后執(zhí)行。通過實現(xiàn)IActionFilter該接口來自定義Action過濾器。action執(zhí)行后, 通過model(Business Model or Data Model)去處理用戶輸入并準備對應的Action Result。
4.Result Execution (返回執(zhí)行結果階段)- 該階段主要包含以下步驟:
- Result filters(結果過濾器) 在(OnResultExecuting)之前 (OnResultExecuted)之后執(zhí)行。 IResultFilter 接口提供兩個方法 OnResultExecuting 、OnResultExecuted分別對應在ActionResult之前和之后執(zhí)行。可以通過實現(xiàn)IResultFilter接口來自定義結果過濾器。
- Action Result是BLL或者DAL對用戶輸入執(zhí)行相應的操作后的返回結果。Action Result 的類型可以是 ViewResult, PartialViewResult, RedirectToRouteResult, RedirectResult, ContentResult,
JsonResult, FileResult, EmptyResult。這些返回類型可以分為兩類,即ViewResult類型和 NonViewResult 類型。ViewResult 類型主要用于返回并渲染html頁面到瀏覽器。NonViewResult僅僅返回數(shù)據(jù),比如文本、二進制、json 格式數(shù)據(jù)。
4.1 View Initialization and Rendering (視圖初始化及渲染)- 可以分解為以下幾個步驟:
- ViewResult 類型,比如 view、partial view 都是實現(xiàn)了 IView (System.Web.Mvc.IView) 接口并由相應的視圖引擎進行渲染。
- 這一過程主要由視圖引擎的 IViewEngine (System.Web.Mvc.IViewEngine) 接口負責。默認ASP.NET MVC 提供了WebForm、Razor 兩種視圖引擎??梢酝ㄟ^實現(xiàn) IViewEngine 創(chuàng)建自定義的視圖引擎并注冊自定義視圖引擎到ASP.NET MVC應用程序。
- Html Helpers 主要用來創(chuàng)建html輸入控件,基于路由創(chuàng)建鏈接,創(chuàng)建ajax表帶等等。Html Helpers 是 HtmlHelper的擴展類并可以很好的進行進一步擴展。 在復雜的情形中,可以渲染一個有前端驗證機制的JavaScript或jquery驗證。
Q15. 解釋下 ASP.NET MVC 的路由機制?
Ans. 路由是一種模式匹配系統(tǒng),用來監(jiān)視傳入的請求并決定如何處理請求。在運行時,路由引擎使用路由表去匹配傳入的請求的Url,根據(jù)路由表定義的Url格式與傳入的Url格式進行匹配??梢栽?code>Application_Start 事件中注冊一個或多個Url格式到路由表中。
當路由引擎在路由表中找到一個與傳入的Url請求匹配的路由記錄,路由引擎會轉發(fā)請求到對應的Controller、Action中。如果沒有匹配的記錄,則返回404。
大致處理流程如下圖:

Q16. 如何在ASP.NET MVC中定義路由?
Ans. 可以參照下面代碼定義路由:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // Route Pattern
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}// Default values for above defined parameters
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
//TODO:
}
需要注意的是路由名稱必須是唯一命名不可重復。
在以上的例子中我們定義一個{controller}/{action}/{id} 這樣的路由并為Controller、Action、id參數(shù)提供了默認值。如果你的url中未包含某一項值,路由引擎會用定義的路由的默認值填充。
假設你的web應用程序掛載在 www.example.com,那么你的url應該是www.example.com/{controller}/{action}/{id}這樣的。
下面是針對定義的路由的匹配結果:

Note: 總是將特殊的路由定義在路由的最上邊。因為路由系統(tǒng)是從上往下對傳入的請求進行匹配,如果有一個匹配上,就不會繼續(xù)往下尋找路由進行匹配。
PS: 這里推薦一個很實用的路由檢查插件RouteDebugger,進行路由的分析。
使用方法很簡單:
1.在對應的mvc項目上通過Nuget包安裝RouteDebugger即可。
2.運行項目,就可以在網(wǎng)頁的下方,可以看到羅列的路由定義及匹配到的路由。效果如圖:

3.可以通過web.config的AppSettings節(jié)點的<add key="RouteDebugger:Enabled" value="true" />進行禁用。
Q17. 什么是特性路由,如何定義特性路由?
Ans. ASP.NET MVC5 、WEB API 2 支持的一種新路由的方式,叫做attribute routing(特性路由)。這種路由方式中,特性被用來定義路由,特性路由使我們能夠更好的控制URLs,支持直接在action和controller上定義路由。
- Controller level routing (控制器級別路由)– 可以為一個controller定義路由,那么它所以的action都將應用此路由,除非一個特定的路由被直接定義在某一個action上。
[RoutePrefix("MyHome")]
[Route("{action=index}")] //default action
public class HomeController : Controller
{
//new route: /MyHome/Index
public ActionResult Index()
{
return View();
}
//new route: /MyHome/About
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
//new route: /MyHome/Contact
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
- Action level routing (Action級別路由)– 可以通過在action上定義action級別路由,那么這個action將被應用這個特定的路由。
public class HomeController : Controller
{
[Route("users/{id:int:min(100)}")] //route: /users/100
public ActionResult Index(int id)
{
//TO DO:
return View();
}
[Route("users/about")] //route" /users/about
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
//route: /Home/Contact
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Note:
- 特性路由應該在基于約定的路由之前配置。
- 如果同時使用特性路由和基于約定的路由,若action上未定義特性路由,那么action將按照基于約定的路由進行路由。在上面的示例中
Contact()action將應用基于約定的路由,即/Home/Contact。 - 當僅僅定義了特性路由而沒有基于約定的路由時,若某個action未定義特性路由時,該action將不能被成功路由,會返回404。
Q18. 什么時候使用特性路由?
Ans. 基于約定的路由一般用來支持確定的URI格式,常見于RESTful APIs。但是通過特性路由相對來說更加簡單的去定義URI格式。
例如,資源通常包含子資源,像客戶擁有訂單,電影有演員,書籍有作者等。通常會創(chuàng)建URIS去反應這種關系,類似/clients/1/orders。
這種類型的URI用基于約定的路由是很難定義的。即使可以定義,如果有很多controllers、資源類型,那定義的路由也將差強人意。
使用特性路由,就會非常簡單定義此類路由,只需要在controller的action上添加一個attribute即可。
[Route("clients/{clientId}/orders")]
public IEnumerable<Order> GetOrdersByClient(int clientId)
{
//TO DO
}
Q19. 如何啟用特性路由?
Ans. 通過在RouteConfig.cs文件的RegisterRoutes()方法中添加routes.MapMvcAttributeRoutes()調用即可。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//enabling attribute routing
routes.MapMvcAttributeRoutes();
}
}
特性路由和基于約定的路由可以同時使用。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//enabling attribute routing
routes.MapMvcAttributeRoutes();
//convention-based routing
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id =
UrlParameter.Optional });
}
}
Q20. 如何在Area中定義特性路由?
Ans. 通過為Area中的Controller定義 RouteArea特性。當你為一個Area中的所有controller定義了特性路由,那就可以刪除為這個area注冊路由的AreaRegistration 類。
[RouteArea("Admin")]
[RoutePrefix("menu")]
[Route("{action}")]
public class MenuController : Controller
{
// route: /admin/menu/login
public ActionResult Login()
{
return View();
}
// route: /admin/menu/products
[Route("products")]
public ActionResult GetProducts()
{
return View();
}
// route: /categories
[Route("~/categories")]
public ActionResult Categories()
{
return View();
}
}
Q21. 路由與URL重寫的區(qū)別是什么?
Ans. 路由和Url重寫都可以用來定義出SEO友好型的URLS。但是它們的實現(xiàn)方式是十分不同的,主要區(qū)別在:
- URL rewriting(URL重寫)注重將一個URL映射到另一個URL。 而Routing(路由)注重將一個URL映射到一個資源。
- URL rewriting(URL重寫)重寫你的舊的URL到一個新的URL。而Routing(路由)只是將URL映射到它對應的原始路由。
Q22. 什么是 Route Constraints (路由約束)?
Ans. Route constraints(路由約束)是對已定義路由進行一些驗證的方式。假設我們已經(jīng)定義了以下路由:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // Route Pattern
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
} // Default values for parameters
);
}
當我們希望限制傳入請求的Url中的Id參數(shù)是數(shù)學類型的,可以采用以下方式:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // Route Pattern
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}, // Default values for parameters
new { id = @"\d+" } //Restriction for id(限制Id未數(shù)字類型)
);
}
這樣對路由定義后,就限制了如果有第三個參數(shù)id,id必須為數(shù)字類型。只有類似http://example.com/Admin/Product/1這樣的Url才能成功路由。
Q23. 路由表是如何創(chuàng)建的?
Ans. 當Mvc應用程序第一次啟動時,global.asax類中的Application_Start() 方法調用RegisterRoutes()方法。RegisterRoutes()方法負責創(chuàng)建了路由表。