• 白话学习MVC(二)页面周期一


     1、概要

    当我们新建一个MVC项目时,打开他的Web.Config文件可以发现

        <httpModules>
          <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> 我们知道ScriptModule 类就是管理用于 ASP.NET 中 AJAX 功能的 HTTP 模块,在此我们不做介绍
          <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> //这个UrlRoutingModule类才是重点
        </httpModules>

    这个HttpModule,拦截所有请求,对请求进行处理,最终创建和执行合适的处理请求的HttpHandler(MVC3之后,这个UrlRoutingModule集成到MVC程序集中了)。

      当客户端在本地浏览器上输入网址来请求咱们的一个MVC程序时,服务端接收到请求.....此处省略N个字(和asp.net处理一样).....

      HttpApplication的事件注册,即将 UrlRoutingModule 注册到HttpApplication的事件中

    public class UrlRoutingModule : IHttpModule
    {
        protected virtual void Init(HttpApplication application)
        {   //开始只是把要执行的具体方法注册到事件中,等待事件被触发时,在执行已被注册的方法。
            application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache); 
            application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
        }
    }

      注册完事件之后,那么就要开始执行HttpApplication事件。

    1、执行Global.asax文件中Application_Start方法。
    即:在此处将一个自己定义的路由规则注册到路由集合中。这个路由集合可以由RouteTable.Routes获得。

     protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                RegisterRoutes(RouteTable.Routes);
            }
    
    public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                routes.MapRoute(
                    "Default", // 路由名称
                    "{controller}/{action}/{id}", // 带有参数的 URL
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
                ); //在路由表里添加一条路由规则
            }

    2、依次执行HttpApplication的事件。  

    BeginRequest
    AuthenticateRequest
    PostAuthenticateRequest
    AuthorizeRequest
    PostAuthorizeRequest
    ResolveRequestCache
    PostResolveRequestCache 在UrlRoutingModule类中,在此事件中注册了一个执行方法,即:OnApplicationPostResolveRequestCache
    PostMapRequestHandler                                                       OnApplicationPostMapRequestHandler
    AcquireRequestState
    PostAcquireRequestState
    PreRequesHandlerExecute
    PostRequeshandlerExecute
    ReleaseRequesState
    PostReleaseRequestState
    UpdateRequestCache
    PostUpdateRequestCach
    LogRequest
    PostLogRequest
    EndRequest
    OnApplicationPostResolveRequestCache方法
    private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
    {
        HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
        this.PostResolveRequestCache(context);
    }
    //这里用HttpContextWrapper类包装当前的HttpContext,实质上也是一个请求的上下文。他可以使用诸如Typemock Isolator或Rhino Mocks的Mock对象框进行模拟变得更简单。
    //并把这个包装之后的上下文作为PostResolveRequestCache的参数
    
    ------------------------------------------------------------------------------------------------------------------
    
    public virtual void PostResolveRequestCache(HttpContextBase context)
    {
        RouteData routeData = this.RouteCollection.GetRouteData(context);
    //GetRouteData方法内部遍历路由集合中的每个路由对象去和上下文中指定的请求URL去匹配。如成功,就返回当前的路由对象RouteData,如不成功,返回null
    //this.RouteCollection就是RouteTable.Routes,即:路由集合。
    
        if (routeData != null)----这里便是判断是否匹配成功
        {
            IRouteHandler routeHandler = routeData.RouteHandler;----//获取一个处理当前匹配成功的路由的对象
         //这个routeHandler其实就是一个MvcRouteHandle类
         ----因为在第一句中执行的GetRouteData方法中,为RouteData的RouteHandler属性赋值为MvcRouteHandler if (routeHandler == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0])); } if (!(routeHandler is StopRoutingHandler)) { RequestContext requestContext = new RequestContext(context, routeData);//把当前的请求的信息和与当前请求匹配成功的路由信息再包装起来。 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); //根据包装后的请求信息,最终得到一个MvcHandler
           ---MvcRouteHandler中只有一个方法,GetHttpHandler方法,返回MvcHandler,Mvc中处理请求的类。
           ---PageRouteHandler中也只有一个方法,GetHttpHandler方法,返回的是Page,asp.net中处理请求的类。 
    MvcRouteHandler
        public class MvcRouteHandler : IRouteHandler
        {
    
            protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                return new MvcHandler(requestContext);
            }
    
            IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
            {
                return this.GetHttpHandler(requestContext);
            }
        }
              if (httpHandler == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object[] { routeHandler.GetType() }));
                }
                //RequestData类是UrlRoutingModule类中嵌套的一个私有类,把处理请求的类和当前请求的虚拟路径
                RequestData data2 = new RequestData {
                    OriginalPath = context.Request.Path,
                    HttpHandler = httpHandler
                };
                context.Items[_requestDataKey] = data2;
                //把封装的处理类MvcHandler和请求的虚拟路径,赋值到 HttpContextWrapper类中。(这样在用到处理类时,就需要实例化,直接取值即可)
                //HttpContextWrapper类包装当前的HttpContext,实质上也是一个请求的上下文。
                context.RewritePath("~/UrlRouting.axd");
    context.RemapHandler(httpHandler);//博客是里这里是这么一句,但是我反编译没找到,可能是版本的问题吧!
    //将MvcHandler 实例 映射到管线中(通常我们是利用web.config 进行配置的,但是MvcHandler 没有默认无参构造函数,所以直接通过向其传递一个实例进行映射)
    } } } 
    OnApplicationPostMapRequestHandler方法
    该方法做的事情很简单,就是重写下请求路径,让输出的路径和输入的路径相同,在这里用来记忆输入路径的是context.Items[],从上下两段代码中可以看到.
    这个事件负责根据文件扩展名映射到具体的httphandle处理类,而MVC的URL信息没有具体的文件后缀名 为了使处理模块能够在iis7中实现路由,则采取了这么一种简单的解决办法。先把路径指向~/UrlRouting.axd,在此事件中会设置一个UrlRouting.axd类型的Handler避免报错,并在下一步事件中替换掉此处的Handler再把~/UrlRouting.axd这个路径给改回来。
    View Code
    private void OnApplicationPostMapRequestHandler(object sender, EventArgs e)
    {
        HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);
        this.PostMapRequestHandler(context);
    }
    
     
    
     public virtual void PostMapRequestHandler(HttpContextBase context)
    {
        RequestData data = (RequestData) context.Items[_requestDataKey];
        if (data != null)
        {
            context.RewritePath(data.OriginalPath);
            context.Handler = data.HttpHandler;
        }
    }
    
     

    上文中得到了一个MvcHandler类实例,MvcHandler继承实现了IHttpAsyncHandler, IHttpHandler, IRequiresSessionState三个接口。而这三个接口如果都实现了,在MVC框架下是不是任何http请求就可以通吃了吗?从MSDN我们得知,事实不是这样的:注意,即使 MvcHandler 实现 IHttpHandler,也不能将其映射为处理程序(例如.mvc 文件扩展名),因为该类不支持无参数构造函数。 (它唯一的构造函数需要一个 RequestContext 对象)
    但是,还好,我们还有MvcHttpHandler。

    如你所知,MvcHttpHandler可以“弥补”MvcHandler的不足,为什么这样说呢?因为MvcHandler没有无参的构造函数,因此即使MvcHandler实现了 IHttpHandler接口,在IIS中也不能将其映射为某类文件扩展名的处理程序,而MvcHttpHandler就提供了不通过路由模块的情况下直接处理映射的处理程序。

    • MvcHttpHandler.使用此处理程序可便于实现直接处理程序映射(不通过路由模块)。如果要将文件扩展名(如 .mvc)直接映射到一个 MVC 处理程序,此处理程序将非常有用。在内部,MvcHttpHandler 将执行 ASP.NET 路由通常执行(通过 MvcRouteHandlerMvcHandler)的任务。但是,它是作为处理程序而不是作为模块来执行这些任务的。对所有请求启用 UrlRoutingModule 时,通常不使用此处理程序。

    • MvcHandler.此处理程序负责启动 MVC 应用程序的 ASP.NET 管道。它从 MVC 控制器工厂接收 Controller 实例;此控制器处理请求的进一步处理。请注意,即使 MvcHandler 实现了 IHttpHandler,它也不能映射为处理程序(例如,针对 .mvc 文件扩展名),因为该类不支持无参数构造函数(而处理程序要求是无参数构造函数)。(其唯一的构造函数需要 RequestContext 对象。)

     3、HttpApplication事件继续执行

    BeginRequest
    AuthenticateRequest
    PostAuthenticateRequest
    AuthorizeRequest
    PostAuthorizeRequest
    ResolveRequestCache
    PostResolveRequestCache 
    PostMapRequestHandler                                                       
    AcquireRequestState
    PostAcquireRequestState
    PreRequesHandlerExecute
    PostRequeshandlerExecute
    ReleaseRequesState
    PostReleaseRequestState
    UpdateRequestCache
    PostUpdateRequestCach
    LogRequest
    PostLogRequest
    EndRequest

    在11-12个事件的时候拿到第7个事件的时候创建的MVCHandler对象执行他的ProcessRequest方法。

    public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
    {
        protected virtual void ProcessRequest(HttpContext httpContext)
        {
            //使用HttpContextWrapper对HttpContext进行封装,封装的目的是为了解耦以获得可测试性.然后从RequestContext.RouteData中提取Controller名称.
            HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
            this.ProcessRequest(httpContext2);
        }
        
        protected internal virtual void ProcessRequest(HttpContextBase httpContext)
        {
            IController controller;
            IControllerFactory controllerFactory;
            this.ProcessRequestInit(httpContext, out controller, out controllerFactory); //获取到Controler实例       ----下节详细介绍
            try
            {
                    controller.Execute(this.RequestContext); //当前Controler对象的Action的创建与执行                   ----下节详细介绍   
    执行包括:加载TempData, 创建及执行Action,处理Action返回的ActionResult ,保存TempData数据。
    } finally { controllerFactory.ReleaseController(controller); //释放当前Controler对象 } } }

    流程如下图,MvcHandler实例来处理请求,他做为处理的主干,当完成之后,释放当前的Controler实例,继续执行HttpApplication事件

    MvcHandler_1

     此图摘自:http://www.cnblogs.com/terrysun/archive/2010/03/19/1690052.html

  • 相关阅读:
    YARN架构设计详解
    HDFS文件上传
    HDFS Namenode启动过程
    (转)计算机原理学习(1)-- 冯诺依曼体系和CPU工作原理
    (转)python之from_bytes、to_bytes
    (转)Python3入门之线程threading常用方法
    (转)Python3.5 queue模块详解
    (转) argparse — 解析命令参数和选项
    (转)Python3之pickle模块
    (转)linux用户态和内核态理解
  • 原文地址:https://www.cnblogs.com/wupeiqi/p/2978660.html
Copyright © 2020-2023  润新知