zoukankan      html  css  js  c++  java
  • 探索ASP.NET MVC框架之路由系统

    引言

      对于ASP.NET MVC的路由系统相信大家肯定不陌生。今天我们就深入ASP.NET的框架内部来看一下路由系统到底是怎么通过我们给出的地址(例如:/Home/Index)解析出Controller和Action。今天的这一篇文章我们就深入框架内部,看看里面的流程。

    UrlRouteModule介绍

      ASP.NET MVC本质上是通过IHttpModule和IHttpHandler两个组件对ASP.NET框架进行扩展来实现的。ASP.NET 请求处理过程是基于管道模型的,这个管道模型是由多个HttpModule和HttpHandler组成,ASP.NET 把http请求依次传递给管道中各个HttpModule,最终被HttpHandler处理,处理完成后,再次经过管道中的HTTP模块,把结果返回给客户端。我们可以在每个HttpModule中都可以干预请求的处理过程。

      ASP.NET MVC就是通过一个自定义IHttpModule将Http请求成功ASP.NET处理管道中接管到MVC框架的。微软自己实现了这个自定义的IHttpModule,这就是我们今天要介绍的UrlRouteModule。这个类是在System.Web.Routing.dll中的。我们通过ILSpy来查看其源码。源码如下(源码经过适当筛选):

     1 public class UrlRoutingModule : IHttpModule
     2     {
     3         private static readonly object _contextKey = new object();
     4         private RouteCollection _routeCollection;
     5         public RouteCollection RouteCollection
     6         {
     7             get
     8             {
     9                 if (this._routeCollection == null)
    10                 {
    11                     this._routeCollection = RouteTable.Routes;
    12                 }
    13                 return this._routeCollection;
    14             }
    15             [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    16             set
    17             {
    18                 this._routeCollection = value;
    19             }
    20         }
    21         public UrlRoutingModule()
    22         {
    23         }
    24         
    25         protected virtual void Init(HttpApplication application)
    26         {
    27             if (application.Context.Items[UrlRoutingModule._contextKey] != null)
    28             {
    29                 return;
    30             }
    31             application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey;
    32             application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
    33         }
    34         
    35         public virtual void PostResolveRequestCache(HttpContextBase context)
    36         {
    37             RouteData routeData = this.RouteCollection.GetRouteData(context);
    38             if (routeData == null)
    39             {
    40                 return;
    41             }
    42             IRouteHandler routeHandler = routeData.RouteHandler;
    43             if (routeHandler == null)
    44             {
    45                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
    46             }
    47             if (routeHandler is StopRoutingHandler)
    48             {
    49                 return;
    50             }
    51             RequestContext requestContext = new RequestContext(context, routeData);
    52             context.Request.RequestContext = requestContext;
    53             IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    54             if (httpHandler == null)
    55             {
    56                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
    57                 {
    58                     routeHandler.GetType()
    59                 }));
    60             }
    61             if (!(httpHandler is UrlAuthFailureHandler))
    62             {
    63                 context.RemapHandler(httpHandler);
    64                 return;
    65             }
    66             if (FormsAuthenticationModule.FormsAuthRequired)
    67             {
    68                 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
    69                 return;
    70             }
    71             throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
    72         }
    73         private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
    74         {
    75             HttpApplication httpApplication = (HttpApplication)sender;
    76             HttpContextBase context = new HttpContextWrapper(httpApplication.Context);
    77             this.PostResolveRequestCache(context);
    78         }
    79         
    80         protected virtual void Dispose()
    81         {
    82         }
    83     }

      我们看到UrlRouteModule继承自IHttpModule接口。这个接口非常简单,只有Init方法和Dispose方法。我们知道ASP.NET处理管线在处理请求过程中,会有19个事件可以让程序员自定义扩展,以便请求在执行完某一个步骤后,可以进行相关操作。UrlRouteModule通过Init方法注册PostResolveRequestCache事件处理函数,让管线处理到PostResolveRequestCache这一步时,调用我们的回调函数OnApplicationPostResolveRequestCache。下面我们来好好分析这个回调函数(代码的73行开始)。

      分析PostResolveRequestCache方法

      我们看到源代码中的红色部分就是回调函数的主体。第一行代码如下:

    1 RouteData routeData = this.RouteCollection.GetRouteData(context);

      我们看到它根据当前请求的上下文,来获取RouteData对象。我们进入GetRouteData看看里面的逻辑是什么。请看源码:

     1 public RouteData GetRouteData(HttpContextBase httpContext)
     2 {
     3     using (this.GetReadLock())
     4     {
     5         foreach (RouteBase current in this)
     6         {
     7             RouteData routeData = current.GetRouteData(httpContext);
     8             if (routeData != null)
     9             {
    10                 RouteData result;
    11                 if (!current.RouteExistingFiles)
    12                 {
    13                     if (!flag2)
    14                     {
    15                         flag = this.IsRouteToExistingFile(httpContext);
    16                     }
    17                     if (flag)
    18                     {
    19                         result = null;
    20                         return result;
    21                     }
    22                 }
    23                 result = routeData;
    24                 return result;
    25             }
    26         }
    27     }
    28     return null;
    29 }

      我们看到该方法内部使用循环来依次调用GetRouteData方法(代码的第7行)。很显然这边的this指的就是我们在程序中配置的路由表了。还记得PostResolveRequestCache方法中的this.RouteCollection吗?回到第一个代码片段第11行,我们看到如下代码(注意红色部分):

     1 public RouteCollection RouteCollection
     2         {
     3             get
     4             {
     5                 if (this._routeCollection == null)
     6                 {
     7                     this._routeCollection = RouteTable.Routes;
     8                 }
     9                 return this._routeCollection;
    10             }
    11             [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    12             set
    13             {
    14                 this._routeCollection = value;
    15             }
    16         }

      看到这,我们应该明白RouteCollection里面存储的是什么了吧!里面存储的就是配置的所有的路由。我们继续往下探索,从current.GetRouteData(httpContext)开始,我们再次进入current对象(类型是RouteBase)的GetRouteData对象。我们看到了RouteBase的源码:

     1 public abstract class RouteBase
     2 {
     3     private bool _routeExistingFiles = true;
     4     public bool RouteExistingFiles
     5     {
     6         get
     7         {
     8             return this._routeExistingFiles;
     9         }
    10         set
    11         {
    12             this._routeExistingFiles = value;
    13         }
    14     }
    15     public abstract RouteData GetRouteData(HttpContextBase httpContext);
    16     public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
    17 }

      RouteBase是一个抽象类,GetRouteData方法的实现是根据具体的继承类型的实现为基础的。那么我们不禁要问哪一个类型继承自RouteBase呢?我们想想看,既然RouteTable.Routes里面存储的都是路由对象,那么我们添加路由对象时,添加的应该就是继承自RouteBase类型的派生类。想必这个大家一定不陌生。我们添加路由的时候除了使用MapRoute方法也可以使用Add方法。如下所示:

    1 routes.MapRoute("StaticRoute", "Content/CustomerJS.js",
    2                             new { controller = "Home", action = "Index" },
    3                             new string[] { "MyFirstMvcProject.Controllers" });
    4 
    5 routes.Add("first", new Route("{controller}/{action}", new MvcRouteHandler()));

      我们看到MVC框架内部就是使用Route来继承RouteBase的。那么很显然,current.GetRouteData调用的肯定是派生类的方法,我们去Route对象中看看这个方法的具体实现。源码如下:

     1 public override RouteData GetRouteData(HttpContextBase httpContext)
     2         {
     3             string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
     4             RouteValueDictionary routeValueDictionary = this._parsedRoute.Match(virtualPath, this.Defaults);
     5             if (routeValueDictionary == null)
     6             {
     7                 return null;
     8             }
     9             RouteData routeData = new RouteData(this, this.RouteHandler);
    10             if (!this.ProcessConstraints(httpContext, routeValueDictionary, RouteDirection.IncomingRequest))
    11             {
    12                 return null;
    13             }
    14             foreach (KeyValuePair<string, object> current in routeValueDictionary)
    15             {
    16                 routeData.Values.Add(current.Key, current.Value);
    17             }
    18             if (this.DataTokens != null)
    19             {
    20                 foreach (KeyValuePair<string, object> current2 in this.DataTokens)
    21                 {
    22                     routeData.DataTokens[current2.Key] = current2.Value;
    23                 }
    24             }
    25             return routeData;
    26         }

      我们看到在这个方法中,创建了RouteData对象,并且对URL路径进行了解析(this._parsedRoute.Match(virtualPath, this.Defaults)这一句)。并且后续还验证了路由规则的正则表达式和命名空间。同时我们也看到RouteData很多属性值都是在这里添加的。RouteData的RouteHandler也是取自Route的RouteHandler属性。

      到此为止,我们终于看清楚RouteData对象是如何创建的,属性值是如何进行赋值的。我们还是回到UrlRouteModule。我们看下面的代码:

     1 IRouteHandler routeHandler = routeData.RouteHandler;
     2 if (routeHandler == null)
     3 {
     4     throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
     5 }
     6 if (routeHandler is StopRoutingHandler)
     7 {
     8     return;
     9 }
    10 RequestContext requestContext = new RequestContext(context, routeData);
    11 context.Request.RequestContext = requestContext;
    12 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

      我们看到下一步的操作就是获取RouteData中保存的RouteHandler,通过RouteHandler来找到下一步处理请求的Handler。我们这次通过route.MapRoute方法来探索。下面我们一起来看下我们注册路由时经常使用的MapRoute的内部实现是怎么样的?请看源码:

     1 public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
     2         {
     3             if (routes == null)
     4             {
     5                 throw new ArgumentNullException("routes");
     6             }
     7             if (url == null)
     8             {
     9                 throw new ArgumentNullException("url");
    10             }
    11 
    12             Route route = new Route(url, new MvcRouteHandler())
    13             {
    14                 Defaults = CreateRouteValueDictionary(defaults),
    15                 Constraints = CreateRouteValueDictionary(constraints),
    16                 DataTokens = new RouteValueDictionary()
    17             };
    18 
    19             if ((namespaces != null) && (namespaces.Length > 0))
    20             {
    21                 route.DataTokens["Namespaces"] = namespaces;
    22             }
    23 
    24             routes.Add(name, route);
    25 
    26             return route;
    27         }

      我们看到我们调用的MapRoute创建了Route对象(原来RouteTable.Routes里面存储的都是Route类型的实例(Route继承自RouteBase对象)),当然IHttpHandler设置为MvcRouteHandler。并且把默认值default、约束、命名空间的值都存储为RouteValueDictionary类型。那么显然RouteData的RouteHandler就是MvcRouteHandler。

      我们知道RouteData的RouteHandler是IRouteHandler类型的,MvcRouteHandler是IRouteHandler的具体实现。我们来看下MvcRouteHandler的源代码:

     1 public class MvcRouteHandler : IRouteHandler
     2     {
     3         private IControllerFactory _controllerFactory;
     4 
     5         public MvcRouteHandler()
     6         {
     7         }
     8 
     9         public MvcRouteHandler(IControllerFactory controllerFactory)
    10         {
    11             _controllerFactory = controllerFactory;
    12         }
    13 
    14         protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    15         {
    16             requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
    17             return new MvcHandler(requestContext);
    18         }
    19 
    20     }

      通过源代码我们看到MvcRouteHandler实现了IRouteHandler接口的GetHttpHandler方法。我们在UrlRouteModule中通过RouteData的RouteHandler属性获取HttpHandler其实调用的就是MvcRouteHandler的GetHttpHandler方法。我们看到最终返回的是MvcHandler类型。到此为止,我们就知道最终返回的IHttpHandler类型就是MvcHandler。请求的后续操作就交给这个HttpHandler处理了。

      相关总结

      1、我们经常使用的MapRoute并不是RouteCollection自带的方法,而是在MVC源码里提供的一个扩展方法。扩展类名是:RouteCollectionExtensions。

      2、Route继承自RouteBase抽象类。在获取RouteData的方法中,遍历RouteTable.Routes集合,将当前的请求的URL和路由模板进行匹配,这一过程实质调用的是Route类型的GetRouteData方法。

      3、RouteData的相关属性和RouteHandler都是从Route对象获取的。

      4、路由系统最终返回的IHttpHandler类型是MvcHandler类型,请求的后续操作就交给这个HttpHandler处理了。

  • 相关阅读:
    caffe常用层: batchNorm层和scale层
    简述configure、pkg-config、pkg_config_path三者的关系
    python删除list中元素的三种方法
    Leetcode 872. Leaf-Similar Trees
    Leetcode 508. Most Frequent Subtree Sum
    Leetcode 572. Subtree of Another Tree
    Leetcode 894. All Possible Full Binary Trees
    Leetcode 814. Binary Tree Pruning
    Leetcode 557. Reverse Words in a String III
    python 多维list声明时的小问题
  • 原文地址:https://www.cnblogs.com/dreamGong/p/5143143.html
Copyright © 2011-2022 走看看