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、渲染视图、返回给客户端,请求结束。

 

posted @ 2012-08-25 18:33  Gerry_hu  阅读(1738)  评论(2编辑  收藏  举报