这几天一直在抽时间算是较深入的了解MVC框架模式M-V-C这三种具体是如何进行交互,在与传统的Codebehind web应用程序相比,URL是如何映射的,以及IView,IViewEngine WebFormViewEngine,VirtualPathProviderViewEngine,WebFormView这几个接口和类具体干些什么事,了解的差不多了,所以要写一篇算是记录和总结.

 

一、首先还是先说一下MVC的URL映射

MVC的地址都是localhost/home/index,完全不同于传统的web应用程序localhost/home/Index.aspx,那MVC这个地址是如何映射出来的呢?

先把结构图贴上来:

mvc03

第一步、Brower发出请求后,这个地址会被MVC路由映射截获(UrlRoutingModule),那这个路由映射的规则我们是可控的,在Global.asax中可对其进行配置,具体的映射规则马上我会贴出来,

也是从网上的各位大牛的文章中直接挪过来,我先说一下具体请求过程,下面继续.由路由映射系统解析过地址以后,会分别解析出ContollerName,ActionName,还有Parameters。

第二步、这个时候就会在Controllers文件夹下我们可以找到HomeController.cs, 这里使用了一个约定, 就是如果URL中获取到的Controller名字是Home, 则他的Controller类名就是HomeController.

在URL中的名字后加上"Controller".

第三步、在HomeController.cs我们可以看到有多个Action,其中包括Index这个Action,在Action中就会调用Model中的方法(Model相当于业务逻辑层)并返回的数据放入ViewData容器.

第四步、数据有了,如何再从Cotorller跳转到View上,就在return View()这句.这个时候就会在Views文件夹下找到Controller的名字Home,然后再找到Action的名字Index,

默义情况下Action的方法名对应一个View,当然上面View("index")可带一个参数,如果把Index改为Default,就会跳到Default页面.

 

二、Routing(这段来自于网上)

1、使用MapRoute方法

这是最简单的为ASP.NET MVC添加识别规则的方法.此方法有如下重载:

MapRoute(string name, string url);
MapRoute(string name, string url, object defaults);
MapRoute(string name, string url, string[] namespaces);
MapRoute(string name, string url, object defaults, object constraints);
MapRoute(string name, string url, object defaults, string[] namespaces);
MapRoute(string name, string url, object defaults, object constraints, string[] namespaces);

name参数

规则名称, 可以随意起名.当时不可以重名,否则会发生错误:
路由集合中已经存在名为“Default”的路由。路由名必须是唯一的。

url参数

url获取数据的规则, 这里不是正则表达式,  将要识别的参数括起来即可, 比如: {controller}/{action}

最少只需要传递name和url参数就可以建立一条Routing(路由)规则.比如实例中的规则完全可以改为:

routes.MapRoute(
"Default",
"{controller}/{action}");

default参数

url参数的默认值.如果一个url只有controller: localhost/home/

而且我们只建立了一条url获取数据规则: {controller}/{action}

那么这时就会为action参数设置defaults参数中规定的默认值. defaults参数是Object类型,所以可以传递一个匿名类型来初始化默认值:

new { controller = "Home", action = "Index" }

实例中使用的是三个参数的MapRoute方法:

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);

constrains参数

用来限定每个参数的规则或Http请求的类型.constraints属性是一个RouteValueDictionary对象,也就是一个字典表, 但是这个字典表的值可以有两种:

  • 用于定义正则表达式的字符串。正则表达式不区分大小写。

    一个用于实现 IRouteConstraint 接口且包含 Match 方法的对象。

    通过使用正则表达式可以规定参数格式,比如controller参数只能为4位数字:

    new { controller = @"\d{4}"}

    通过第IRouteConstraint 接口目前可以限制请求的类型.因为System.Web.Routing中提供了HttpMethodConstraint类, 这个类实现了IRouteConstraint 接口. 我们可以通过为RouteValueDictionary字典对象添加键为"httpMethod", 值为一个HttpMethodConstraint对象来为路由规则添加HTTP 谓词的限制, 比如限制一条路由规则只能处理GET请求:

    httpMethod = new HttpMethodConstraint("GET", "POST")

    完整的代码如下:

    routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = "" }, // Parameter defaults
    new { controller = @"\d{4}" , httpMethod = new HttpMethodConstraint( "GET", "POST" ) }
    );

    当然我们也可以在外部先创建一个RouteValueDictionary对象在作为MapRoute的参数传入, 这只是语法问题.

     

    namespace参数

    此参数对应Route.DataTokens属性. 官方的解释是:

    获取或设置传递到路由处理程序但未用于确定该路由是否匹配 URL 模式的自定义值。

     

    2、MapRoute的实例

    public static void RegisterRoutes(RouteCollection routes) 
     { 
           routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 
    
           #region 酒店频道部分 
            // hotels/list-beijing-100,200-3 
           routes.MapRoute( 
                  "酒店列表页", 
                   "hotels/{action}-{city}-{price}-{star}", 
                   new { controller = "Hotel", action = "list", city = "beijing", price="-1,-1", star="-1" }, 
                   new { city=@"[a-zA-Z]*",price=@"(\d)+\,(\d)+", star="[-1-5]"} 
                  ); 
    
          //hotels/所有匹配 
           routes.MapRoute( 
                    "酒店首页", 
                    "hotels/{*values}", 
                    new { controller = "Hotel", action = "default", hotelid = "" } 
                    ); 
           #endregion 
    
           //网站首页. 
           routes.MapRoute( 
                     "网站首页", 
                     "{*values}", 
                     new { controller = "Home", action = "index"} 
                     );   
     }

    实现的功能:

    (1)访问 localhost/hotels/list-beijing-100,200-3 会访问酒店频道的列表页,并传入查询参数

    (2)访问 localhost/hotels 下面的任何其他页面地址, 都会跳转到酒店首页.

    (3)访问 localhost 下面的任何地址, 如果未匹配上面2条, 则跳转到首页.

    简单总结:

    (1)Routing规则有顺序(按照添加是的顺序), 如果一个url匹配了多个Routing规则, 则按照第一个匹配的Routing规则执行.

    (2)由于上面的规则, 要将具体频道的具体页面放在最上方, 将频道首页 和 网站首页 放在最下方.

    (3) {*values} 表示后面可以使任意的格式.

     

    三、Controller、Action、View

    1、页面处理流程发送请求 –> UrlRoutingModule捕获请求 –> MvcRouteHandler.GetHttpHandler() –> MvcHandler.ProcessRequest()

    2.MvcHandler.ProcessRequest() 处理流程:使用工厂方法获取具体的Controller –> Controller.Execute() –> 释放Controller对象

    3.Controller.Execute() 处理流程获取Action –> 调用Action方法获取返回的ActionResult –> 调用ActionResult.ExecuteResult() 方法

    4.ActionResult.ExecuteResult() 处理流程获取IView对象-> 根据IView对象中的页面路径获取Page类-> 调用IView.RenderView() 方法(内部调用Page.RenderView方法)

    通过对MVC的分析,我们了解到Controller对象的职责是传递数据,获取View对象(实现了IView接口的类),通知View对象显示.View对象的作用是显示.虽然显示的方法RenderView()是由Controller调用的,

    但是Controller仅仅是一个"指挥官"的作用, 具体的显示逻辑仍然在View对象中.需要注意IView接口与具体的ViewPage之间的联系.在Controller和View之间还存在着IView对象.对于ASP.NET程序提供了

    WebFormView对象实现了IView接口.WebFormView负责根据虚拟目录获取具体的Page类,然后调用 Page.RenderView().

     

    四、说一下关键的几个接口和类

    大家看到IView这个接口,从字面来看,是View抽象出来的接口。其实不然,从实际情况来看,真正的View是aspx/ascx,而在MVC中这种页面又是继承ViewPage/ViewUserControl这两个类.那这两者如何串起来的呢?

    在ActionResult方法最后return View()(当然不是全部返回View,还有返回文件和跳转的,这个View是PartialViewResult或者是ViewResult类型),只能返回View方法时才会去找View.

    在PartialViewResult或者是ViewResult中会有个重写的方法FindView,返回ViewEngineResult类,这个ViewEngineResult就是一次查询的结果,并且其中就包括了我们要找的IView以及IViewEngine.

    那IViewEngine是什么.ViewEngine即视图引擎, 在ASP.NET MVC中将ViewEngine的作用抽象成了 IViewEngine 接口.并且定义两个方法,FindPartialView和FindView.实现的类有两个,

    关系是:WebFormViewEngine : VirtualPathProviderViewEngine : IViewEngine

    引擎的作用有两个:

    1.寻找Page/用户控件的路径

    2.根据路径创建IView对象.也就是根据页面的物理文件创建IView接口对象.

    以WebFormViewEngine为例, 在WebFormViewEngine类中定义了 MasterLocationFormats/ViewLocationFormats /PartialViewLocationFormats ,在调用FindPartialView/FindView方法时,

    首先找到View对象的磁盘路径, 然后使用CreatePartialView/CreateView方法将磁盘路径转化实现了IView接口的WebFormView对象.看下WebFormViewEngine构造函数的源代码:

    隐藏行号 复制代码 这是一段程序代码。
    1. public WebFormViewEngine() 
      
    2. { 
      
    3.     base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.master", "~/Views/Shared/{0}.master" }; 
      
    4.     base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.master", "~/Areas/{2}/Views/Shared/{0}.master" }; 
      
    5.     base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.aspx", "~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.aspx", "~/Views/Shared/{0}.ascx" }; 
      
    6.     base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.aspx", "~/Areas/{2}/Views/{1}/{0}.ascx", "~/Areas/{2}/Views/Shared/{0}.aspx", 
      
    7. "~/Areas/{2}/Views/Shared/{0}.ascx" }; 
      
    8.     base.PartialViewLocationFormats = base.ViewLocationFormats; 
      
    9.     base.AreaPartialViewLocationFormats = base.AreaViewLocationFormats; 
      
    10. } 
      
    11. protected override IView CreateView(ControllerContext controllerContext,string viewPath,string masterPath) 
      
    12. { 
      
    13.     return new WebFormView(viewPath, masterPath); 
      
    14. } 
      
    15. public virtual ViewEngineResult FindView(ControllerContext controllerContext,string viewName,string masterName,bool useCache) 
      
    16. { 
      
    17.     string[] strArray; 
      
    18.     string[] strArray2; 
      
    19.     if (controllerContext == null) 
    20.        throw new ArgumentNullException("controllerContext"); 
      
    21.     if (string.IsNullOrEmpty(viewName))
    22.       throw new ArgumentException(MvcResources.Common_NullOrEmpty, "viewName"); 
      
    23.    string requiredString = controllerContext.RouteData.GetRequiredString("controller"); 
      
    24.    string str2 = this.GetPath(controllerContext, this.ViewLocationFormats, this.AreaViewLocationFormats, "ViewLocationFormats", viewName, requiredString, "View", useCache, out strArray); 
      
    25.    string str3 = this.GetPath(controllerContext, this.MasterLocationFormats, this.AreaMasterLocationFormats, "MasterLocationFormats", masterName, requiredString, "Master", useCache, out strArray2); 
      
    26.    if (!string.IsNullOrEmpty(str2) && (!string.IsNullOrEmpty(str3) || string.IsNullOrEmpty(masterName))) 
    1.       return new ViewEngineResult(this.CreateView(controllerContext, str2 , str3 ), this); 
    2.    return new ViewEngineResult(strArray.Union<string>(strArray2)); 
      
    3. } 
      

     

    实现了IView接口的对象也只有一个:WebFormView

    WebFormViewEngine 根据页面路径, 将一个页面地址转化为一个WebFormView对象,也就是一个IView接口对象.至此IView接口和Page页面类仍然没有任何关系,

    IView对象只是保存了页面的物理路径.接着在IView的Render事件中,根据物理路径创建了一个页面的object实例,注意看这一段代码:

    隐藏行号 复制代码 这是一段程序代码。
    1. public virtual void Render(ViewContext viewContext, TextWriter writer)
      
    2. {
      
    3.    if (viewContext == null)
      
    4.       throw new ArgumentNullException("viewContext");
      
    5.    
      
    6.    object obj2 = this.BuildManager.CreateInstanceFromVirtualPath(this.ViewPath, typeof(object));
      
    7.    if (obj2 == null)
      
    8.       throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, MvcResources.WebFormViewEngine_ViewCouldNotBeCreated, new object[] { this.ViewPath }));
      
    9.    ViewPage page = obj2 as ViewPage;
      
    10.    if (page != null)
      
    11.       this.RenderViewPage(viewContext, page);
      
    12.    else
      
    13.    {
      
    14.       ViewUserControl control = obj2 as ViewUserControl;
      
    15.       if (control == null)
      
    16.          throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, MvcResources.WebFormViewEngine_ViewCouldNotBeCreated, new object[] { this.ViewPath }));
      
    17.       this.RenderViewUserControl(viewContext, control);
      
    18.    }
      
    19. }
      

     

  • 只有在这个时候才会转化为ViewPage/ViewUserControl

    以上大概将要记录的要讲的基本写完,这中间肯定有不足之处,如有机会如再完善. 

  • posted on 2013-08-29 11:45  tzj19810812  阅读(523)  评论(0)    收藏  举报