这篇文章我们主要探讨UrlRoutingModule 如何截获HttpApplicatioin的管道事件,从而把Http Request 引入Mvc框架中的.分析这个过程需要我们用到UrlRoutingModule 类的源码.
现在我们可以从微软的官方网站下载.Net4.0的源码.
下载地址:http://blogs.msdn.com/b/rscc/
首先我们在生成一个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
谢谢。
转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html
本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan