本系列主要翻譯自《ASP.NET MVC Interview Questions and Answers 》- By Shailendra Chauhan,想看英文原版的可訪問http://www.dotnettricks.com/free-ebooks自行下載。該書主要分為兩部分,ASP.NET MVC 5、ASP.NET WEB API2。本書最大的特點(diǎn)是以面試問答的形式進(jìn)行展開。通讀此書,會(huì)幫助你對(duì)ASP.NET MVC有更深層次的理解。
由于個(gè)人技術(shù)水平和英文水平也是有限的,因此錯(cuò)誤在所難免,希望大家多多留言指正。
系列導(dǎo)航
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的管道及路由機(jī)制
Q13. Asp.net mvc 中的ViewModel?
Ans. 在 ASP.NET MVC中, ViewModel 是一個(gè)包含將在強(qiáng)類型視圖中展示的字段的類。它是用來將數(shù)據(jù)從Controller傳遞到強(qiáng)類型視圖中。
ViewModel的關(guān)鍵點(diǎn):
- ViewModel 包含在視圖中呈現(xiàn)的字段。(LabelFor, EditorFor, DisplayFor helpers)
- ViewModel 可以通過數(shù)據(jù)注解指定特定的驗(yàn)證規(guī)則。
- ViewModel 可以包含多個(gè)來自不同數(shù)據(jù)模型或數(shù)據(jù)源的實(shí)體或?qū)ο蟆?/li>
Q14. 解釋下 ASP.NET MVC pipeline(管道)?
Ans. 先上圖:
Routing(路由) - 路由是管道的第一步。簡(jiǎn)單來說,它是一種模式匹配系統(tǒng),去路由表中注冊(cè)的Url中匹配傳入的請(qǐng)求。在代碼中主要是
UrlRoutingModule(System.Web.Routing.UrlRoutingModule)
在做匹配的工作,路由表對(duì)應(yīng)的是RouteTable(System.Web.Routing.RouteTable)
。Controller Initialization(初始化控制器) - MvcHandler使用ProcessRequest方法開始對(duì)ASP.NET MVC pipeline進(jìn)行實(shí)時(shí)處理。這個(gè)方法使用工廠類
IControllerFactory
的實(shí)例(默認(rèn)是System.Web.Mvc.DefaultControllerFactory)去創(chuàng)建對(duì)應(yīng)的Controller。Action Execution (Action執(zhí)行)– 該環(huán)節(jié)按以下順序執(zhí)行:
當(dāng)Controller初始化后,Controller通過傳遞選擇的action方法詳情調(diào)用它自己的
InvokeAction()
方法。這一步是由IActionInvoker
處理。當(dāng)選擇合適的action方法后,model binder(模型綁定器,默認(rèn)是
System.Web.Mvc.DefaultModelBinder
)取回傳入的Http請(qǐng)求的數(shù)據(jù),然后進(jìn)行數(shù)據(jù)轉(zhuǎn)換,數(shù)據(jù)驗(yàn)證(比如required、數(shù)據(jù)格式等)。同時(shí)還需要將數(shù)據(jù)映射到action方法對(duì)應(yīng)的參數(shù)上。Authentication Filter (認(rèn)證過濾器)是在ASP.NET MVC5中引入的,它先于authorization filter(授權(quán)過濾器)執(zhí)行。它主要用來對(duì)用戶認(rèn)證。認(rèn)證過濾器處理請(qǐng)求中的用戶憑證并返回相應(yīng)的主體。在ASP.NET MVC5之前,使用 authorization filter (授權(quán)過濾器)對(duì)用戶進(jìn)行認(rèn)證和授權(quán)。 Authenticate attribute(認(rèn)證特性)默認(rèn)是被用來進(jìn)行認(rèn)證. 可以通過實(shí)現(xiàn)
IAuthenticationFilter
接口來創(chuàng)建自定義的authentication filter(認(rèn)證過濾器)Authorization filter(授權(quán)過濾器)用來對(duì)已認(rèn)證的用戶執(zhí)行授權(quán)操作。例如。基于角色的授權(quán)。Authorize attribute(授權(quán)特性默認(rèn)用來執(zhí)行授權(quán)操作)。可以通過實(shí)現(xiàn)
IAuthorizationFilter
接口來創(chuàng)建自定義的authentication filter(授權(quán)過濾器)。Action filters (Action過濾器)在
OnActionExecuting
之前OnActionExecuting
之后執(zhí)行。IActionFilter
接口提供了兩個(gè)方法OnActionExecuting
、OnActionExecuting
分別在action之前和之后執(zhí)行。通過實(shí)現(xiàn)IActionFilter
該接口來自定義Action過濾器。action執(zhí)行后, 通過model(Business Model or Data Model)去處理用戶輸入并準(zhǔn)備對(duì)應(yīng)的Action Result。
4.Result Execution (返回執(zhí)行結(jié)果階段)- 該階段主要包含以下步驟:
- Result filters(結(jié)果過濾器) 在(OnResultExecuting)之前 (OnResultExecuted)之后執(zhí)行。 IResultFilter 接口提供兩個(gè)方法 OnResultExecuting 、OnResultExecuted分別對(duì)應(yīng)在ActionResult之前和之后執(zhí)行。可以通過實(shí)現(xiàn)IResultFilter接口來自定義結(jié)果過濾器。
- Action Result是BLL或者DAL對(duì)用戶輸入執(zhí)行相應(yīng)的操作后的返回結(jié)果。Action Result 的類型可以是 ViewResult, PartialViewResult, RedirectToRouteResult, RedirectResult, ContentResult,
JsonResult, FileResult, EmptyResult。這些返回類型可以分為兩類,即ViewResult類型和 NonViewResult 類型。ViewResult 類型主要用于返回并渲染html頁面到瀏覽器。NonViewResult僅僅返回?cái)?shù)據(jù),比如文本、二進(jìn)制、json 格式數(shù)據(jù)。
4.1 View Initialization and Rendering (視圖初始化及渲染)- 可以分解為以下幾個(gè)步驟:
- ViewResult 類型,比如 view、partial view 都是實(shí)現(xiàn)了 IView (System.Web.Mvc.IView) 接口并由相應(yīng)的視圖引擎進(jìn)行渲染。
- 這一過程主要由視圖引擎的 IViewEngine (System.Web.Mvc.IViewEngine) 接口負(fù)責(zé)。默認(rèn)ASP.NET MVC 提供了WebForm、Razor 兩種視圖引擎。可以通過實(shí)現(xiàn) IViewEngine 創(chuàng)建自定義的視圖引擎并注冊(cè)自定義視圖引擎到ASP.NET MVC應(yīng)用程序。
- Html Helpers 主要用來創(chuàng)建html輸入控件,基于路由創(chuàng)建鏈接,創(chuàng)建ajax表帶等等。Html Helpers 是 HtmlHelper的擴(kuò)展類并可以很好的進(jìn)行進(jìn)一步擴(kuò)展。 在復(fù)雜的情形中,可以渲染一個(gè)有前端驗(yàn)證機(jī)制的JavaScript或jquery驗(yàn)證。
Q15. 解釋下 ASP.NET MVC 的路由機(jī)制?
Ans. 路由是一種模式匹配系統(tǒng),用來監(jiān)視傳入的請(qǐng)求并決定如何處理請(qǐng)求。在運(yùn)行時(shí),路由引擎使用路由表去匹配傳入的請(qǐng)求的Url,根據(jù)路由表定義的Url格式與傳入的Url格式進(jìn)行匹配。可以在Application_Start
事件中注冊(cè)一個(gè)或多個(gè)Url格式到路由表中。
當(dāng)路由引擎在路由表中找到一個(gè)與傳入的Url請(qǐng)求匹配的路由記錄,路由引擎會(huì)轉(zhuǎn)發(fā)請(qǐng)求到對(duì)應(yīng)的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:
}
需要注意的是路由名稱必須是唯一命名不可重復(fù)。
在以上的例子中我們定義一個(gè){controller}/{action}/{id} 這樣的路由并為Controller、Action、id參數(shù)提供了默認(rèn)值。如果你的url中未包含某一項(xiàng)值,路由引擎會(huì)用定義的路由的默認(rèn)值填充。
假設(shè)你的web應(yīng)用程序掛載在 www.example.com,那么你的url應(yīng)該是www.example.com/{controller}/{action}/{id}這樣的。
下面是針對(duì)定義的路由的匹配結(jié)果:
Note: 總是將特殊的路由定義在路由的最上邊。因?yàn)槁酚上到y(tǒng)是從上往下對(duì)傳入的請(qǐng)求進(jìn)行匹配,如果有一個(gè)匹配上,就不會(huì)繼續(xù)往下尋找路由進(jìn)行匹配。
PS: 這里推薦一個(gè)很實(shí)用的路由檢查插件RouteDebugger,進(jìn)行路由的分析。
使用方法很簡(jiǎn)單:
1.在對(duì)應(yīng)的mvc項(xiàng)目上通過Nuget包安裝RouteDebugger即可。
2.運(yùn)行項(xiàng)目,就可以在網(wǎng)頁的下方,可以看到羅列的路由定義及匹配到的路由。效果如圖:
3.可以通過web.config的AppSettings節(jié)點(diǎn)的<add key="RouteDebugger:Enabled" value="true" />
進(jìn)行禁用。
Q17. 什么是特性路由,如何定義特性路由?
Ans. ASP.NET MVC5 、WEB API 2 支持的一種新路由的方式,叫做attribute routing(特性路由)。這種路由方式中,特性被用來定義路由,特性路由使我們能夠更好的控制URLs,支持直接在action和controller上定義路由。
- Controller level routing (控制器級(jí)別路由)– 可以為一個(gè)controller定義路由,那么它所以的action都將應(yīng)用此路由,除非一個(gè)特定的路由被直接定義在某一個(gè)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級(jí)別路由)– 可以通過在action上定義action級(jí)別路由,那么這個(gè)action將被應(yīng)用這個(gè)特定的路由。
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:
- 特性路由應(yīng)該在基于約定的路由之前配置。
- 如果同時(shí)使用特性路由和基于約定的路由,若action上未定義特性路由,那么action將按照基于約定的路由進(jìn)行路由。在上面的示例中
Contact()
action將應(yīng)用基于約定的路由,即/Home/Contact
。 - 當(dāng)僅僅定義了特性路由而沒有基于約定的路由時(shí),若某個(gè)action未定義特性路由時(shí),該action將不能被成功路由,會(huì)返回404。
Q18. 什么時(shí)候使用特性路由?
Ans. 基于約定的路由一般用來支持確定的URI格式,常見于RESTful APIs。但是通過特性路由相對(duì)來說更加簡(jiǎn)單的去定義URI格式。
例如,資源通常包含子資源,像客戶擁有訂單,電影有演員,書籍有作者等。通常會(huì)創(chuàng)建URIS去反應(yīng)這種關(guān)系,類似/clients/1/orders
。
這種類型的URI用基于約定的路由是很難定義的。即使可以定義,如果有很多controllers、資源類型,那定義的路由也將差強(qiáng)人意。
使用特性路由,就會(huì)非常簡(jiǎn)單定義此類路由,只需要在controller的action上添加一個(gè)attribute即可。
[Route("clients/{clientId}/orders")]
public IEnumerable<Order> GetOrdersByClient(int clientId)
{
//TO DO
}
Q19. 如何啟用特性路由?
Ans. 通過在RouteConfig.cs
文件的RegisterRoutes()
方法中添加routes.MapMvcAttributeRoutes()
調(diào)用即可。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//enabling attribute routing
routes.MapMvcAttributeRoutes();
}
}
特性路由和基于約定的路由可以同時(shí)使用。
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特性。當(dāng)你為一個(gè)Area中的所有controller定義了特性路由,那就可以刪除為這個(gè)area注冊(cè)路由的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。但是它們的實(shí)現(xiàn)方式是十分不同的,主要區(qū)別在:
- URL rewriting(URL重寫)注重將一個(gè)URL映射到另一個(gè)URL。 而Routing(路由)注重將一個(gè)URL映射到一個(gè)資源。
- URL rewriting(URL重寫)重寫你的舊的URL到一個(gè)新的URL。而Routing(路由)只是將URL映射到它對(duì)應(yīng)的原始路由。
Q22. 什么是 Route Constraints (路由約束)?
Ans. Route constraints(路由約束)是對(duì)已定義路由進(jìn)行一些驗(yàn)證的方式。假設(shè)我們已經(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
);
}
當(dāng)我們希望限制傳入請(qǐng)求的Url中的Id參數(shù)是數(shù)學(xué)類型的,可以采用以下方式:
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ù)字類型)
);
}
這樣對(duì)路由定義后,就限制了如果有第三個(gè)參數(shù)id,id必須為數(shù)字類型。只有類似http://example.com/Admin/Product/1
這樣的Url才能成功路由。
Q23. 路由表是如何創(chuàng)建的?
Ans. 當(dāng)Mvc應(yīng)用程序第一次啟動(dòng)時(shí),global.asax
類中的Application_Start()
方法調(diào)用RegisterRoutes()
方法。RegisterRoutes()
方法負(fù)責(zé)創(chuàng)建了路由表。