[转载]Asp.net MVC源码分析--UrlRoutingModule与Service location的亲密接触

[转载]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

View Code

 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

View Code

 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

View Code

 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 方法.

View Code

 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

View Code

 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();

View Code

 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

谢谢。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏