• MVC请求处理过程的本质之个人总结


    本文纯属个人见解,是对前面学习的总结,如有描述不正确的地方还请高手指正~

    废话就不多说了,开始。。。

    首先在网站第一次被请求时会创建一个Application实例,贴源码先

        if (application == null)
        {
            application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
            using (new ApplicationImpersonationContext())
            {
                application.InitInternal(context, this._state, this._eventHandlerMethods);
            }
        }

     通过反射MVC的项目中Global.asax得到的是一个MvcApplication实例,先看实例创建时的过程

    在Global.asax文件的Application_Start()方法是这样的

    protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                RegisterGlobalFilters(GlobalFilters.Filters);
                RegisterRoutes(RouteTable.Routes);
            }

    其中红色标记出的这句作用是注册路由表,看看这个方法到底具体做了什么,上代码(以MVC默认项目代码为例)

            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                routes.MapRoute(
                    "Default", // Route name
                    "{controller}/{action}/{id}", // URL with parameters
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
                );
            }

    IgnoreRoute与MapRoute方法都是定义在ASP.NET MVC中,基于RouteCollection类型的扩展方法,都会向RouteCollection中添加一个Route对象,而这个Route对象在匹配成功时将返回RouteData对象,这里可以添加多个MapRoute,每个的Route name必须不一样,在请求时会依次匹配,最先匹配成功的返回RouteData对象后将不再继续匹配。

    这里要详细说下MapRoute方法,先看下MVC内部是怎么实现的

    public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
               ……(此处代码省略)
    
                Route route = new Route(url, new MvcRouteHandler()) {
                    Defaults = new RouteValueDictionary(defaults),
                    Constraints = new RouteValueDictionary(constraints),
                    DataTokens = new RouteValueDictionary()
                };
    
              ……(此处代码省略)
    
                return route;
            }

    在此方法中创建了一个Route对象,传递给Route的参数中有本次请求的Url和一个MvcRouteHandler对象,注意此处是创建了一个新的MvcRouteHandler

    继续追踪,看下这个handler最终给了谁

    通过反编译软件可以看到Route类的构造函数有如下代码

    public Route(string url, IRouteHandler routeHandler)
    {
        this.Url = url;
        this.RouteHandler = routeHandler;
    }

    可以看出MvcRouteHandler最终给了Route对象的RouteHandler属性

    写到这里先总结一下:

    第一次请求创建MvcApplication实例时在Application_Start()方法中注册了路由表,并且每个路由表都创建了一个新的MvcRouteHandler实例并赋给了Route对象的RouteHandler属性。

    ok,这一步就到此结束。

    回到一开始的代码段,创建好Application实例后执行的是Application的InitInternal方法,在看下这个方法做了什么

    internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
    {
        
       ……//其他的代码省略,只看最主要的  
       this.InitModules();
      ……
    
    }

      然后继续跟踪InitModules方法

    private void InitModules()
    {
       //这里创建了一个HttpModules的集合
    this._moduleCollection = RuntimeConfig.GetAppConfig().HttpModules.CreateModules(); this.InitModulesCommon(); }

      先不管集合中都有什么,再跟踪InitModulesCommon方法

    private void InitModulesCommon()
    {
        int count = this._moduleCollection.Count;
        for (int i = 0; i < count; i++)
        {
            this._currentModuleCollectionKey = this._moduleCollection.GetKey(i);
            this._moduleCollection[i].Init(this);
        }
        this._currentModuleCollectionKey = null;
        this.InitAppLevelCulture();
    }

    也就是说得到application实例后又执行了所有注册了的HttpModules的Init方法

    那就来看看集合中有什么特殊的,打开这个路径C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config

    找到web.config文件,在httpModules节点下有这样一行代码

     <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />

    这个System.Web.Routing.UrlRoutingModule正是我们要讨论的

    UrlRoutingModule的Init方法如下

    protected virtual void Init(HttpApplication application)
    {
        if (application.Context.Items[_contextKey] == null)
        {
            application.Context.Items[_contextKey] = _contextKey;
            application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
        }
    }

      从中可以看到它为Http请求管道的第七个事件追加了事件,找到这个方法体

    public virtual void PostResolveRequestCache(HttpContextBase context)
    {
        RouteData routeData = this.RouteCollection.GetRouteData(context);
        if (routeData != null)
        {
            IRouteHandler routeHandler = routeData.RouteHandler;
            ……//此处省略
            IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
            ……//此处省略
            context.RemapHandler(httpHandler);
         ……//此处省略
    } }

      到这里就跟第一步接轨了,首先根据上下文得到请求地址,并匹配路由表,匹配成功后得到一个RouteData,然后拿到RouteData的RouteHandler属性中存放的MvcRouteHandler实例,然后得到HttpHandler实例,最后将http请求重定向到该handler实例。

    到这里算是为真正的响应请求做足了准备,现在在Http请求管道第七个事件之后与第八个事件之间得到的handler实例就是我们通过路由规则匹配到的MvcRouteHandler实例。

    然后到第十一个事件执行该handler实例的PR方法,内部创建控制器工厂、创建控制器、执行Action、对视图进行渲染、最后输出Response流给客户端。请求结束。

    大总结:

    首先创建路由表,并为每个路由表创建一个MvcRouteHandler实例,注册http请求管道的第七个事件,http请求到第七个事件的时候会触发注册的事件,匹配路由表,得到一个对应的MvcRouteHandler实例,然后得到httphandler,最后将请求重新定向到该handler,在第七个事件与第八个事件之间得到该handler实例,在第十一个事件执行该handler实例的PR方法,执行Action、渲染视图、返回给客户端,请求结束。

     

  • 相关阅读:
    Sublime text 2/3 中 Package Control 的安装与使用方法
    http content-type accept的区别
    div布局
    [转]HDFS HA 部署安装
    Hive内置数据类型
    MyBatis注解select in参数
    HTTP协议状态码详解(HTTP Status Code)
    Hive基础(5)---内部表 外部表 临时表
    Hive基础(4)---Hive的内置服务
    MySQL数据备份之mysqldump使用(转)
  • 原文地址:https://www.cnblogs.com/huzhenchao/p/2656797.html
Copyright © 2020-2023  润新知