[转载]Asp.net MVC源码分析–UrlRoutingModule与Service location的亲密接触 – 十一月的雨 – 博客园.
这篇文章我们主要探讨UrlRoutingModule 如何截获HttpApplicatioin的管道事件,从而把Http Request 引入Mvc框架中的.分析这个过程需要我们用到UrlRoutingModule 类的源码.
现在我们可以从微软的官方网站下载.Net4.0的源码.
下载地址:http://aspnet.codeplex.com/releases/view/58781
首先我们在生成一个ASP.NET MVC3.0 的项目时都会在Global.asax文件中生成一个MvcApplication类,在这个类中我们可以在Application_Start方法中注册我们定义好的的路由规则.
MvcApplication.cs
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 public static void RegisterGlobalFilters(GlobalFilterCollection filters) 4 { 5 filters.Add(new HandleErrorAttribute()); 6 } 7 8 public static void RegisterRoutes(RouteCollection routes) 9 { 10 routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 12 routes.MapRoute( 13 "Default", // Route name 14 "{controller}/{action}/{id}", // URL with parameters 15 new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults 16 ); 17 18 } 19 protected void Application_Start() 20 { 21 AreaRegistration.RegisterAllAreas(); 22 23 RegisterGlobalFilters(GlobalFilters.Filters); 24 RegisterRoutes(RouteTable.Routes); 25 } 26 } 27
我们看一下routes.MapRoute的方法实现,在这个方法中我们向RouteTable.Routes全局的路由表中加入我们自定义的路由规则.
RouteCollectionExtensions.cs
1 [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing characters.")] 2 public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { 3 if (routes == null) { 4 throw new ArgumentNullException("routes"); 5 } 6 if (url == null) { 7 throw new ArgumentNullException("url"); 8 } 9 10 Route route = new Route(url, new MvcRouteHandler()) { 11 Defaults = new RouteValueDictionary(defaults), 12 Constraints = new RouteValueDictionary(constraints), 13 DataTokens = new RouteValueDictionary() 14 }; 15 16 if ((namespaces != null) && (namespaces.Length > 0)) { 17 route.DataTokens["Namespaces"] = namespaces; 18 } 19 20 routes.Add(name, route); 21 22 return route; 23 }
这时我们需要注意的是在创建Route对象的时候我们传入的是MvcRouteHandler对象.这个对象在什么时候使用呢? 我们需要查看UrlRoutingModule的源码.
UrlRoutingModule.cs
1 public class UrlRoutingModule : IHttpModule { 2 3 protected virtual void Init(HttpApplication application) { 4 5 application.PostResolveRequestCache += OnApplicationPostResolveRequestCache; 6 } 7 8 private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) { 9 HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context); 10 PostResolveRequestCache(context); 11 } 12 13 public virtual void PostResolveRequestCache(HttpContextBase context) { 14 // Match the incoming URL against the route table 15 RouteData routeData = RouteCollection.GetRouteData(context); 16 17 // Do nothing if no route found 18 if (routeData == null) { 19 return; 20 } 21 22 // If a route was found, get an IHttpHandler from the route's RouteHandler 23 IRouteHandler routeHandler = routeData.RouteHandler; 24 25 RequestContext requestContext = new RequestContext(context, routeData); 26 27 // Remap IIS7 to our handler 28 context.RemapHandler(httpHandler); 29 } 30 }
我们看到当UrlRoutingModule初始化并调用Init方法的时候注册了HttpApplication的 PostResolveRequestCache管道事件,所以当HttpAplication对象(这里是MvcApplication)执行时就会触 发PostResolveRequestCache事件,从而把HttpRequest引导进MVC module中,接下来我们看一下Mvc是怎么处理Request的.
我们看到在PostResolveRequestCache方法中有:
RouteData routeData = RouteCollection.GetRouteData(context);
这里会通过GetRouteData找到与当前请求的URL 匹配的RouteData 没有则返回。
RouteCollection.cs
1 public RouteData GetRouteData(HttpContextBase httpContext) { 2 if (httpContext == null) { 3 throw new ArgumentNullException("httpContext"); 4 } 5 if (httpContext.Request == null) { 6 throw new ArgumentException(SR.GetString(SR.RouteTable_ContextMissingRequest), "httpContext"); 7 } 8 9 // Optimize performance when the route collection is empty. The main improvement is that we avoid taking 10 // a read lock when the collection is empty. Without this check, the UrlRoutingModule causes a 25%-50% 11 // regression in HelloWorld RPS due to lock contention. Robbin:The UrlRoutingModule is now in the root web.config, 12 // so we need to ensure the module is performant, especially when you are not using routing. 13 // This check does introduce a slight bug, in that if a writer clears the collection as part of a write 14 // transaction, a reader may see the collection when it's empty, which the read lock is supposed to prevent. 15 // We will investigate a better fix in Dev10 Beta2. The Beta1 bug is Dev10 652986. 16 if (Count == 0) { 17 return null; 18 } 19 20 if (!RouteExistingFiles) { 21 string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath; 22 if ((requestPath != "~/") && 23 (_vpp != null) && 24 (_vpp.FileExists(requestPath) || 25 _vpp.DirectoryExists(requestPath))) { 26 // If we're not routing existing files and the file exists, we stop processing routes 27 return null; 28 } 29 } 30 31 // Go through all the configured routes and find the first one that returns a match 32 using (GetReadLock()) { 33 foreach (RouteBase route in this) { 34 RouteData routeData = route.GetRouteData(httpContext); 35 if (routeData != null) { 36 return routeData; 37 } 38 } 39 } 40 41 return null; 42 }
————————————————————————————————-
接下来我们看到:
IRouteHandler routeHandler = routeData.RouteHandler;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
context.RemapHandler(httpHandler);
以上几句代码完成的就是把我们在Application_Start方法中注册的路由规则取得,然后找到MvcRoutingHandler,再调用GetHttpHandler方法
取到IHttpHandler.
接下来的逻辑是:
1.iis7下把HttpHandler它注册到IIS的workprocesser中.
2.iis6中把HttpHandler放到HttpContext.Handler中.
最终在HttpApplication.PreRequestHandlerExcute后执行这个HttpHandler.
HttpContext.cs/请参照HttpContext.RemapHandler 方法.
1 public void RemapHandler(IHttpHandler handler) { 2 IIS7WorkerRequest wr = _wr as IIS7WorkerRequest; 3 4 if (wr != null) { 5 // Remap handler not allowed after ResolveRequestCache notification 6 if (_notificationContext.CurrentNotification >= RequestNotification.MapRequestHandler) { 7 throw new InvalidOperationException(SR.GetString(SR.Invoke_before_pipeline_event, "HttpContext.RemapHandler", "HttpApplication.MapRequestHandler")); 8 } 9 10 string handlerTypeName = null; 11 string handlerName = null; 12 13 if (handler != null) { 14 Type handlerType = handler.GetType(); 15 16 handlerTypeName = handlerType.AssemblyQualifiedName; 17 handlerName = handlerType.FullName; 18 } 19 20 wr.SetRemapHandler(handlerTypeName, handlerName); 21 } 22 23 _remapHandler = handler; 24 } 25
———————————————————————————————–
下面我们看一下MvcRoutingHandler的源码.我们看到GetHttpHandler 方法最终返回的是MvcHandler对象.也就是终我们放到
HttpContext.Handler 的对象。最终会调用这个Handler.ProcessRequest 方法来处理HttpRequest请求.
MvcRoutingHandler.cs
1 public class MvcRouteHandler : IRouteHandler { 2 private IControllerFactory _controllerFactory; 3 4 public MvcRouteHandler() { 5 } 6 7 public MvcRouteHandler(IControllerFactory controllerFactory) { 8 _controllerFactory = controllerFactory; 9 } 10 11 protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { 12 requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext)); 13 return new MvcHandler(requestContext); 14 } 15 16 protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) { 17 string controllerName = (string)requestContext.RouteData.Values["controller"]; 18 IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); 19 return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName); 20 } 21 22 #region IRouteHandler Members 23 IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) { 24 return GetHttpHandler(requestContext); 25 } 26 #endregion 27 }
———————————————————————————————-
MvcHandler.ProcessRequest 方法中调用了ProcessRequestInit方法,这里面就用到了ControllerBuilder.GetControllerFactory();
1 private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) { 2 // If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks 3 // at Request.Form) to work correctly without triggering full validation. 4 bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current); 5 if (isRequestValidationEnabled == true) { 6 ValidationUtility.EnableDynamicValidation(HttpContext.Current); 7 } 8 9 AddVersionHeader(httpContext); 10 RemoveOptionalRoutingParameters(); 11 12 // Get the controller type 13 string controllerName = RequestContext.RouteData.GetRequiredString("controller"); 14 15 // Instantiate the controller and call Execute 16 factory = ControllerBuilder.GetControllerFactory(); 17 controller = factory.CreateController(RequestContext, controllerName); 18 if (controller == null) { 19 throw new InvalidOperationException( 20 String.Format( 21 CultureInfo.CurrentCulture, 22 MvcResources.ControllerBuilder_FactoryReturnedNull, 23 factory.GetType(), 24 controllerName)); 25 } 26 }
至此IIS的request就进入Mvc 的处理流程,接下来的Service Location工作,请看我的另外一篇文章
DependencyResolver与Service Location — http://www.cnblogs.com/RobbinHan/archive/2011/11/30/2269537.html
谢谢。