• Asp.net MVC源码分析UrlRoutingModule与Service location的亲密接触


    这篇文章我们主要探讨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

    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

    谢谢。

    转载请注明出处:http://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html 

    本文作者: 十一月的雨 http://www.cnblogs.com/RobbinHan

  • 相关阅读:
    通过crontab命令创建任务
    linux 通过at命令创建任务
    在linux中如何实现定时发送邮件到指定邮箱,监测任务
    python发送邮件
    序列化分析
    文件写入
    导入excel成一个list集合不支持大文件倒入(优化点在于分批分线程导入)
    react重学
    关于java集合排序
    fiddler还是浏览器的问题
  • 原文地址:https://www.cnblogs.com/RobbinHan/p/2270707.html
Copyright © 2020-2023  润新知