Asp.net mvc 知多少(二)

本系列主要翻译自《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 知多少(十)

本节主要讲解MVC的管道及路由机制


Q13. Asp.net mvc 中的ViewModel?
Ans. 在 ASP.NET MVC中, ViewModel 是一个包含将在强类型视图中展示的字段的类。它是用来将数据从Controller传递到强类型视图中。
ViewModel的关键点:

  • ViewModel 包含在视图中呈现的字段。(LabelFor, EditorFor, DisplayFor helpers)
  • ViewModel 可以通过数据注解指定特定的验证规则。
  • ViewModel 可以包含多个来自不同数据模型或数据源的实体或对象。

Q14. 解释下 ASP.NET MVC pipeline(管道)?
Ans. 先上图:

ASP.NET MVC pipeline
  1. Routing(路由) - 路由是管道的第一步。简单来说,它是一种模式匹配系统,去路由表中注册的Url中匹配传入的请求。在代码中主要是UrlRoutingModule(System.Web.Routing.UrlRoutingModule)在做匹配的工作,路由表对应的是RouteTable(System.Web.Routing.RouteTable)

  2. Controller Initialization(初始化控制器) - MvcHandler使用ProcessRequest方法开始对ASP.NET MVC pipeline进行实时处理。这个方法使用工厂类IControllerFactory的实例(默认是System.Web.Mvc.DefaultControllerFactory)去创建对应的Controller。

  3. Action Execution (Action执行)– 该环节按以下顺序执行:

  • 当Controller初始化后,Controller通过传递选择的action方法详情调用它自己的InvokeAction()方法。这一步是由IActionInvoker处理。

  • 当选择合适的action方法后,model binder(模型绑定器,默认是System.Web.Mvc.DefaultModelBinder)取回传入的Http请求的数据,然后进行数据转换,数据验证(比如required、数据格式等)。同时还需要将数据映射到action方法对应的参数上。

  • Authentication Filter (认证过滤器)是在ASP.NET MVC5中引入的,它先于authorization filter(授权过滤器)执行。它主要用来对用户认证。认证过滤器处理请求中的用户凭证并返回相应的主体。在ASP.NET MVC5之前,使用 authorization filter (授权过滤器)对用户进行认证和授权。 Authenticate attribute(认证特性)默认是被用来进行认证. 可以通过实现IAuthenticationFilter接口来创建自定义的authentication filter(认证过滤器)

  • Authorization filter(授权过滤器)用来对已认证的用户执行授权操作。例如。基于角色的授权。Authorize attribute(授权特性默认用来执行授权操作)。可以通过实现IAuthorizationFilter接口来创建自定义的authentication filter(授权过滤器)。

  • Action filters (Action过滤器)在 OnActionExecuting之前OnActionExecuting之后执行。IActionFilter接口提供了两个方法 OnActionExecutingOnActionExecuting分别在action之前和之后执行。通过实现IActionFilter 该接口来自定义Action过滤器。

  • action执行后, 通过model(Business Model or Data Model)去处理用户输入并准备对应的Action Result。

4.Result Execution (返回执行结果阶段)- 该阶段主要包含以下步骤:

  • Result filters(结果过滤器) 在(OnResultExecuting)之前 (OnResultExecuted)之后执行。 IResultFilter 接口提供两个方法 OnResultExecuting 、OnResultExecuted分别对应在ActionResult之前和之后执行。可以通过实现IResultFilter接口来自定义结果过滤器。
  • Action Result是BLL或者DAL对用户输入执行相应的操作后的返回结果。Action Result 的类型可以是 ViewResult, PartialViewResult, RedirectToRouteResult, RedirectResult, ContentResult,
    JsonResult, FileResult, EmptyResult。这些返回类型可以分为两类,即ViewResult类型和 NonViewResult 类型。ViewResult 类型主要用于返回并渲染html页面到浏览器。NonViewResult仅仅返回数据,比如文本、二进制、json 格式数据。

4.1 View Initialization and Rendering (视图初始化及渲染)- 可以分解为以下几个步骤:

  • ViewResult 类型,比如 view、partial view 都是实现了 IView (System.Web.Mvc.IView) 接口并由相应的视图引擎进行渲染。
  • 这一过程主要由视图引擎的 IViewEngine (System.Web.Mvc.IViewEngine) 接口负责。默认ASP.NET MVC 提供了WebForm、Razor 两种视图引擎。可以通过实现 IViewEngine 创建自定义的视图引擎并注册自定义视图引擎到ASP.NET MVC应用程序。
  • Html Helpers 主要用来创建html输入控件,基于路由创建链接,创建ajax表带等等。Html Helpers 是 HtmlHelper的扩展类并可以很好的进行进一步扩展。 在复杂的情形中,可以渲染一个有前端验证机制的JavaScript或jquery验证。

Q15. 解释下 ASP.NET MVC 的路由机制?
Ans. 路由是一种模式匹配系统,用来监视传入的请求并决定如何处理请求。在运行时,路由引擎使用路由表去匹配传入的请求的Url,根据路由表定义的Url格式与传入的Url格式进行匹配。可以在Application_Start 事件中注册一个或多个Url格式到路由表中。
当路由引擎在路由表中找到一个与传入的Url请求匹配的路由记录,路由引擎会转发请求到对应的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参数提供了默认值。如果你的url中未包含某一项值,路由引擎会用定义的路由的默认值填充。
假设你的web应用程序挂载在 www.example.com,那么你的url应该是www.example.com/{controller}/{action}/{id}这样的。
下面是针对定义的路由的匹配结果:

路由匹配结果

Note: 总是将特殊的路由定义在路由的最上边。因为路由系统是从上往下对传入的请求进行匹配,如果有一个匹配上,就不会继续往下寻找路由进行匹配。

PS: 这里推荐一个很实用的路由检查插件RouteDebugger,进行路由的分析。
使用方法很简单:
1.在对应的mvc项目上通过Nuget包安装RouteDebugger即可。
2.运行项目,就可以在网页的下方,可以看到罗列的路由定义及匹配到的路由。效果如图:

RouteDebugger效果图

3.可以通过web.config的AppSettings节点的<add key="RouteDebugger:Enabled" value="true" />进行禁用。


Q17. 什么是特性路由,如何定义特性路由?
Ans. ASP.NET MVC5 、WEB API 2 支持的一种新路由的方式,叫做attribute routing(特性路由)。这种路由方式中,特性被用来定义路由,特性路由使我们能够更好的控制URLs,支持直接在action和controller上定义路由。

  1. 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();
 }
}
  1. 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格式。
例如,资源通常包含子资源,像客户拥有订单,电影有演员,书籍有作者等。通常会创建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重写的区别是什么?
Ans. 路由和Url重写都可以用来定义出SEO友好型的URLS。但是它们的实现方式是十分不同的,主要区别在:

  • URL rewriting(URL重写)注重将一个URL映射到另一个URL。 而Routing(路由)注重将一个URL映射到一个资源。
  • URL rewriting(URL重写)重写你的旧的URL到一个新的URL。而Routing(路由)只是将URL映射到它对应的原始路由。

Q22. 什么是 Route Constraints (路由约束)?
Ans. Route constraints(路由约束)是对已定义路由进行一些验证的方式。假设我们已经定义了以下路由:

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参数是数学类型的,可以采用以下方式:

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未数字类型)
 );
}

这样对路由定义后,就限制了如果有第三个参数id,id必须为数字类型。只有类似http://example.com/Admin/Product/1这样的Url才能成功路由。


Q23. 路由表是如何创建的?
Ans. 当Mvc应用程序第一次启动时,global.asax类中的Application_Start() 方法调用RegisterRoutes()方法。RegisterRoutes()方法负责创建了路由表。

推荐阅读更多精彩内容