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