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处理了。

  • 相关阅读:
    Linux 分卷压缩
    用 virtualenv 创建隔离的 Python 运行环境
    Ubuntu 16.04 安装 Python3.6
    Python 的全局变量
    Git 使用总结
    开源的许可证GPL、LGPL、BSD、Apache 2.0
    Python Unofficial Package Site
    apt-get update 更新 ubuntu时出现Hash sum mismatch的原因及解决方法
    Keep It Simple & Stupid
    Python IDLE 增加清屏功能
  • 原文地址:https://www.cnblogs.com/dreamGong/p/5143143.html
Copyright © 2011-2022 走看看