白话ASP.NET MVC之一:Url 路由


       好久没有写关于ASP.NET MVC的东西了,虽然《ASP.NET MVC4框架揭秘》已经完完整整的看完一遍,但是感觉和一锅粥差不多,没什么可写的,因为我自己不理解,也就写不出来。现在开始看《ASP.NET MVC5框架揭秘》,应该说第二遍了,每个代码都调试了,也看了很多的源代码,突然有一种清新的感觉,很多东西都连起来了,原来是这样啊,不不经意发出这样的感叹。既然有了一个好的理解,就整理一下,写出来,也就算巩固学习了。

      言归正传吧,很多学习Asp.Net MVC的人把整个MVC请求处理的过程人为地划分成了几个小系统,分法很多了,我比较中意划分方法的是:Url路由,Controller激活,Action执行。一个请求进来,必须要经过路由系统处理,生成必要的数据,比如:Controller的名字,Action的名字,路由系统获得了Controller的名字,才会有后面的Controller的激活系统,激活了Controller,然后执行Action,返回处理结果给客户,整个流程就结束了。但是每个部分里面又包含了很多辅助的小系统来完成相应的工作。Controller的激活和Actionde执行以后再说吧,今天我们就先来说说Url路由。

      本文章里面不打算翻译一个个大家都知道的名词,比如:Controller,Action,ModelBinder,ActionInvoker等等众多类型,直接用英文单词,因为翻译成中文有时候很难表示完整的意思。

一、简介

      Url路由:在ASP.NET MVC系统里,来自客户端的请求总是指向一个定义在某个Controller类型中的某个Action方法,并且目标Controller和Action的名称由请求的Url决定,既URL驱动的,所以必须采取某种机制根据请求的Url地址把目标的Controller和Action的名称解析出来,我们将这种机制就称为“路由(Routing)”。但是我们要说明的是这个路由系统是独立的,不是专属ASP.NET MVC的。独立的意思是可以在ASP.NET WEB FORMS里使用,可以在ASP.NET MVC里面使用,因为路由系统专门针对MVC的特点扩展了其原有的路由系统的实现。所以我把ASP.NET的路由系统分成两个部分,可能说法不太准确,我这样分是方便我更好的理解,大家可以自行分解,便于理解就好。

         第一:ASP.NET路由系统,定义在System.Web.dll程序集中,命名空间是System.Web.Routing,这个可以认为是针对ASP.NET WEB FORMS的,路由设置里面要写映射的物理.aspx文件,具体详情可以自行研究,就不多说了。

protected void Application_Start(object sender, EventArgs e)
{
      var defaults = new RouteValueDictionary{ {"name","*" }, {"id","*" } };
      RouteTable.Routes.MapPageRoute("","employees/{name}/{id}","~/Default.aspx",true,defaults);
}

         第二:针对ASP.NET MVC扩展出来的新的路由系统,定义在System.Web.MVC.dll程序集里面。扩展类是定义在命名空间System.Web.Mvc下的RouteCollectionExtensions类型,路由注册的时候要写Controller和Action了。

public static void RegisterRoutes(RouteCollection routes)
 {
       routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Employees", action = "GetAllEmployees", id = UrlParameter.Optional }
            );
 }

       我们知道ASP.NET MVC是通过扩展ASP.NET处理管道实现的,这里面有两个最重要的组件,一个是实现了IHttpModule接口的UrlRoutingModule,此组件用于截获请求,进行路由解析,并重新Remap到请求的处理程序上,这个处理程序就是第二个组件,实现了IHttpHandler的MvcHandler,此组件用于激活Controller和Action方法的执行。可以这样说,路由系统的解析操作就发生在UrlRoutingModule组件里面。我们先看看他的代码,然后我们按着请求的先后顺序一步一步的介绍所涉及到的对象。

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

   二、路由解析的先后顺序

         路由规则注册-----》截获请求-----》路由解析并获得RouteData对象-----》根据RouteData的RouteHandler获得MvcRouteHandler对象-----》根据MvcRouteHandler获得MvcHandler对象-----》请求对象重新路由,HttpContext.Remap(MvcHandler)-----》MvcHandler接管请求处理-----》Controller激活-----》Action方法的执行-----》返回处理结果并结束

       上面是路由解析的全过程,我再用白话描述一遍:要想解析路由,必须先注册路由规则的对象吧,也就是Route对象,连注册都没有还解析个什么劲啊,我们一般在Global.asax文件的Application_Start方法里面注册Route对象。注册好了路由规则,启动系统,早已注册好的UrlRoutingModule截获请求,用当前的请求的Url和路由表【RouteTable】里面存储的路由对象【Route】进行比较,其实是Url地址和路由对象【Route】的路由地址模板Url进行匹配,没有匹配就返回空值,如果多个匹配,就选择第一个匹配路由对象【Route】,根据选择的路由对象【Route】生成路由数据【RouteData】。因为路由数据【RouteData】包含RouteHandler属性,RouteHandler属性用于提供最终处理请求的HttpHandler,ASP.NET MVC中RouteHandler的属性值就是MvcRouteHandler,MvcRouteHandler实现了IRouteHandler接口,这个接口有一个方法GetHttpHandler,这个方法就提供了用于处理最终请求的HttpHandler,这个HttpHandler就是MvcHandler,好了,该获取的对象都准备好了,那就把请求交给MvcHandler吧,交接是通过HttpContext.Remap方实现的,好了,大概就是这么一个过程。

      我先简要的把路由解析所涉及到的类型说一下,我们是面向对象编程的,所以很多东西已经对象化了,说的不错。哈哈老王卖瓜了:

       1、Route:路由规则抽象获得RouteBase类型,此类型是抽象类,他有唯一的一个子类就是Route对象,路由规则对象肯定要有路由模板的地址Url,要有路由的默认值了,路由的约束值了等等一些东西。也可以这样理解,我们注册的每一个规则就是一个Route对象,每一个Route对象实例就是代表一种路由的规则。

       2、UrlRoutingModule:我们有了Route路由规则对象,也注册好了,系统启动,我们要把请求截获,不截获请求,就没办法处理了,所以我们就是扩展了ASP.Net处理管道,实现了IHttpModule接口,定义了UrlRoutingModule类型,它用于截获请求,进行路由解析,我上面也提到过该类,并贴出了代码,下面会详细说的,非常核心的类,如果对ASP.NET处理管道不熟悉的可以去网上查找一些资料,很容易找的。

       3、RouteTable:ASP.NET MVC有一个代表全局的路由表,所有注册Route对象都存在RouteTable.Routes表示的集合里面,路由解析的时候就是和RouteTable.Routes表示的路由表里面的每一个Route对象进行匹配,如果RouteTable里面的所有Route对象所代表的路由规则和当前的Url都不匹配就返回Null值,如果有多个匹配就选择第一个匹配的Route对象,并根据Route对象生成RouteData对象。

      4、RouteData:当请求的Url和RouteTable路由表中表示路由规则的Route相匹配的时候,会根据匹配的Route对象生成RouteData,它里面包含了根据Url解析所得到东西,如:controller的名字,Action的名字等信息。

      5、MvcRouteHandler:MvcRouteHandler是MvcHandler对象的提供对象,RouteData的RouteHandler属性的值针对MVC来说就是MvcRouteHandler,如果是ASP.NET的路由系统,那就是PageRouteHandler对象了。

      6、MvcHandler:既然我们获得了MvcHandler,通过HttpContext的Remap方法重新路由,把请求交给MvcHandler来处理,后面就是Controller激活和Action方法的解析了。

      好了,简单的说了一下每个对象的用途,大家也许有了一个大概的印象了吧,我们下面就来详细的说说每一个对象的实际情况。

三、路由对象的详解

     我们使用的是面向对象的语言,所操作的一切都是对象,路由规则经过抽象就是RouteBase对象,有了RouteBase对象,我们才可以注册路由对象,才有针对RouteBase的路由解析。接下来就让我们开始说我们的第一个对象吧,Route路由对象,刚才不是说要说RouteBase,咱们现在又要说Route对象了,怎么变了,其实没变,两个对象是一回事。RouteBase其实是 一个抽象类,我们所指的或者所说的Route路由对象,其实都是从RouteBase对象继承下来的。

      1、RouteBase和Route

        我们看看RouteBase的源代码吧,不看源代码,很多东西不能搞清楚的。

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

  RouteBase有两个抽象方法,第一个是返回的是RouteData类型的GetRouteData方法,RouteData说白了就是路由数据,在白点就是根据路由规则解析获得的数据,所以方法名是GetRouteData,该方法的参数是HttpContextBase对象,这个对象表示的当前请求。或者说这个方法就是根据当前的Url请求和路由规则对象进行比较,如果匹配就根据路由规则对象Route生成对象的RouteData路由数据对象。另外一个抽象方法就是,返回类型为VirtualPathData对象的GetVirtualPath方法,此方法的作用就是根据提供的数据和注册的路由规则生成相应的虚拟路径。RouteData稍后会将,让我们看看VirtualPathData是一个什么样的东西,源代码如下:

 1 public class VirtualPathData
 2 {
 3         private string _virtualPath;
 4 
 5         private RouteValueDictionary _dataTokens = new RouteValueDictionary();
 6 
 7         public RouteValueDictionary DataTokens
 8         {
 9             get
10             {
11                 return this._dataTokens;
12             }
13         }
14 
15         public RouteBase Route
16         {
17             get;
18             set;
19         }
20 
21         public string VirtualPath
22         {
23             get
24             {
25                 return this._virtualPath ?? string.Empty;
26             }
27             set
28             {
29                 this._virtualPath = value;
30             }
31         }
32 
33         public VirtualPathData(RouteBase route, string virtualPath)
34         {
35             this.Route = route;
36             this.VirtualPath = virtualPath;
37         }
38  }

返回字符串类型VirtualPath属性就是生成虚拟路径,返回类型RouteBase的Route属性表示的匹配规则那个RouteBase对象。现在我想访问真实存在的一个物理文件怎么办呢?RouteBase有一个RouteExistingFiles属性,这个属性表示是否路由物理存在的文件,默认值是True,意味着我们想访问某个物理文件在不改变设置的情况下是不行的,因为已经按着路由规则发生了路由了。

    我们在来看看Route对象吧,源码如下:

  1 public class Route : RouteBase
  2 {
  3         private const string HttpMethodParameterName = "httpMethod";
  4 
  5         private string _url;
  6 
  7         private ParsedRoute _parsedRoute;
  8 
  9         public RouteValueDictionary Constraints
 10         {
 11             get;
 12             set;
 13         }
 14 
 15         public RouteValueDictionary DataTokens
 16         {
 17             get;
 18             set;
 19         }
 20 
 21         public RouteValueDictionary Defaults
 22         {
 23             get;
 24             set;
 25         }
 26 
 27         public IRouteHandler RouteHandler
 28         {
 29             get;
 30             set;
 31         }
 32 
 33         public string Url
 34         {
 35             get
 36             {
 37                 return this._url ?? string.Empty;
 38             }
 39             set
 40             {
 41                 this._parsedRoute = RouteParser.Parse(value);
 42                 this._url = value;
 43             }
 44         }
 45 
 46         public Route(string url, IRouteHandler routeHandler)
 47         {
 48             this.Url = url;
 49             this.RouteHandler = routeHandler;
 50         }
 51 
 52         public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
 53         {
 54             this.Url = url;
 55             this.Defaults = defaults;
 56             this.RouteHandler = routeHandler;
 57         }
 58 
 59         public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
 60         {
 61             this.Url = url;
 62             this.Defaults = defaults;
 63             this.Constraints = constraints;
 64             this.RouteHandler = routeHandler;
 65         }
 66 
 67         public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
 68         {
 69             this.Url = url;
 70             this.Defaults = defaults;
 71             this.Constraints = constraints;
 72             this.DataTokens = dataTokens;
 73             this.RouteHandler = routeHandler;
 74         }
 75 
 76         public override RouteData GetRouteData(HttpContextBase httpContext)
 77         {
 78             string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
 79             RouteValueDictionary routeValueDictionary = this._parsedRoute.Match(virtualPath, this.Defaults);
 80             if (routeValueDictionary == null)
 81             {
 82                 return null;
 83             }
 84             RouteData routeData = new RouteData(this, this.RouteHandler);
 85             if (!this.ProcessConstraints(httpContext, routeValueDictionary, RouteDirection.IncomingRequest))
 86             {
 87                 return null;
 88             }
 89             foreach (KeyValuePair<string, object> current in routeValueDictionary)
 90             {
 91                 routeData.Values.Add(current.Key, current.Value);
 92             }
 93             if (this.DataTokens != null)
 94             {
 95                 foreach (KeyValuePair<string, object> current2 in this.DataTokens)
 96                 {
 97                     routeData.DataTokens[current2.Key] = current2.Value;
 98                 }
 99             }
100             return routeData;
101         }
102 
103         public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
104         {
105             BoundUrl boundUrl = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints);
106             if (boundUrl == null)
107             {
108                 return null;
109             }
110             if (!this.ProcessConstraints(requestContext.HttpContext, boundUrl.Values, RouteDirection.UrlGeneration))
111             {
112                 return null;
113             }
114             VirtualPathData virtualPathData = new VirtualPathData(this, boundUrl.Url);
115             if (this.DataTokens != null)
116             {
117                 foreach (KeyValuePair<string, object> current in this.DataTokens)
118                 {
119                     virtualPathData.DataTokens[current.Key] = current.Value;
120                 }
121             }
122             return virtualPathData;
123         }
124 
125         protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
126         {
127             IRouteConstraint routeConstraint = constraint as IRouteConstraint;
128             if (routeConstraint != null)
129             {
130                 return routeConstraint.Match(httpContext, this, parameterName, values, routeDirection);
131             }
132             string text = constraint as string;
133             if (text == null)
134             {
135                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[]
136                 {
137                     parameterName,
138                     this.Url
139                 }));
140             }
141             object value;
142             values.TryGetValue(parameterName, out value);
143             string arg_7C_0 = Convert.ToString(value, CultureInfo.InvariantCulture);
144             string pattern = "^(" + text + ")$";
145             return Regex.IsMatch(arg_7C_0, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
146         }
147 
148         private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection)
149         {
150             if (this.Constraints != null)
151             {
152                 foreach (KeyValuePair<string, object> current in this.Constraints)
153                 {
154                     if (!this.ProcessConstraint(httpContext, current.Value, current.Key, values, routeDirection))
155                     {
156                         return false;
157                     }
158                 }
159                 return true;
160             }
161             return true;
162         }
163   }

  其实代码不复杂,大家也应该看的懂,Route对象直接继承RouteBase对象的,而且是唯一一个这样的对象,既然是路由规则对象,肯定包括,地址模板,默认值,约束条件和一些附加的数据,Constraints保存的就是约束条件,Defaults保存的就是默认值,Url属性就是地址模板了。他一定要实现GetRouteData方法和GetVirtualPath方法

 2、RouteData

    我们有了路由规则了,也就是Route对象,我们也注册了,接下来就是路由解析,就是和Route对象的的Url进行比较,如果匹配就生成了RouteData对象,也就是Route对象GetRouteData方法返回结果了。大家一定要记住,RouteData是基于Route对象生成的,我们看看源码吧:

 1 public class RouteData
 2 {
 3         private IRouteHandler _routeHandler;
 4 
 5         private RouteValueDictionary _values = new RouteValueDictionary();
 6 
 7         private RouteValueDictionary _dataTokens = new RouteValueDictionary();
 8 
 9         public RouteValueDictionary DataTokens
10         {
11             get
12             {
13                 return this._dataTokens;
14             }
15         }
16 
17         public RouteBase Route
18         {
19             get;
20             set;
21         }
22 
23         public IRouteHandler RouteHandler
24         {
25             get
26             {
27                 return this._routeHandler;
28             }
29             set
30             {
31                 this._routeHandler = value;
32             }
33         }
34 
35         public RouteValueDictionary Values
36         {
37             get
38             {
39                 return this._values;
40             }
41         }
42 
43         public RouteData()
44         {
45         }
46 
47         public RouteData(RouteBase route, IRouteHandler routeHandler)
48         {
49             this.Route = route;
50             this.RouteHandler = routeHandler;
51         }
52 
53         public string GetRequiredString(string valueName)
54         {
55             object obj;
56             if (this.Values.TryGetValue(valueName, out obj))
57             {
58                 string text = obj as string;
59                 if (!string.IsNullOrEmpty(text))
60                 {
61                     return text;
62                 }
63             }
64             throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("RouteData_RequiredValue"), new object[]
65             {
66                 valueName
67             }));
68         }
69   }

   RouteData对象是规则匹配所要生成的东西,根据Url解析获得数据存在Values属性里面,DataTokens属性表示一些附加的数据,并且这个数据来源于Route对象的DataTokens属性,RouteData对象RouteHandler属性的值也是来源于Route对象的RouteHandler属性,这个RouteHandler在ASP.NET路由系统就是PageRouteHandler,在ASP.NET MVC中就是MvcRouteHandler,用于提供最终处理请求的HttpHandler。RouteData对象还有一个Route属性,此属性表示在路由解析的时候匹配的那个Route路由规则对象。

    之所以说RouteData是基于Route对象产生了,因为RouteData对象里面的很多值来源于Routed对象,Route对象是基础。

3、RouteTable

     当我们了有了路由规则Route对象的时候,这些路由对象放在什么地方呢?答案就是放在了路由表RouteTable对象中,我们先看看他的源码吧:

 1 public class RouteTable
 2 {
 3         private static RouteCollection _instance = new RouteCollection();
 4 
 5         public static RouteCollection Routes
 6         {
 7             get
 8             {
 9                 return RouteTable._instance;
10             }
11         }
12 }

 RouteTable有一个静态属性是Routes,此属性的类型是RouteCollection,字面意思Route的Collection,就是路由对象的集合,类型如下:

  1 public class RouteCollection : Collection<RouteBase>
  2     {
  3         private class ReadLockDisposable : IDisposable
  4         {
  5             private ReaderWriterLockSlim _rwLock;
  6 
  7             public ReadLockDisposable(ReaderWriterLockSlim rwLock)
  8             {
  9                 this._rwLock = rwLock;
 10             }
 11 
 12             void IDisposable.Dispose()
 13             {
 14                 this._rwLock.ExitReadLock();
 15             }
 16         }
 17 
 18         private class WriteLockDisposable : IDisposable
 19         {
 20             private ReaderWriterLockSlim _rwLock;
 21 
 22             public WriteLockDisposable(ReaderWriterLockSlim rwLock)
 23             {
 24                 this._rwLock = rwLock;
 25             }
 26 
 27             void IDisposable.Dispose()
 28             {
 29                 this._rwLock.ExitWriteLock();
 30             }
 31         }
 32 
 33         private sealed class IgnoreRouteInternal : Route
 34         {
 35             public IgnoreRouteInternal(string url) : base(url, new StopRoutingHandler())
 36             {
 37             }
 38 
 39             public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary routeValues)
 40             {
 41                 return null;
 42             }
 43         }
 44 
 45         private Dictionary<string, RouteBase> _namedMap = new Dictionary<string, RouteBase>(StringComparer.OrdinalIgnoreCase);
 46 
 47         private VirtualPathProvider _vpp;
 48 
 49         private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
 50 
 51         public bool AppendTrailingSlash
 52         {
 53             get;
 54             set;
 55         }
 56 
 57         public bool LowercaseUrls
 58         {
 59             get;
 60             set;
 61         }
 62 
 63         public bool RouteExistingFiles
 64         {
 65             get;
 66             set;
 67         }
 68 
 69         private VirtualPathProvider VPP
 70         {
 71             get
 72             {
 73                 if (this._vpp == null)
 74                 {
 75                     return HostingEnvironment.VirtualPathProvider;
 76                 }
 77                 return this._vpp;
 78             }
 79             set
 80             {
 81                 this._vpp = value;
 82             }
 83         }
 84 
 85         public RouteBase this[string name]
 86         {
 87             get
 88             {
 89                 if (string.IsNullOrEmpty(name))
 90                 {
 91                     return null;
 92                 }
 93                 RouteBase result;
 94                 if (this._namedMap.TryGetValue(name, out result))
 95                 {
 96                     return result;
 97                 }
 98                 return null;
 99             }
100         }
101 
102         public RouteCollection()
103         {
104         }
105 
106         public RouteCollection(VirtualPathProvider virtualPathProvider)
107         {
108             this.VPP = virtualPathProvider;
109         }
110 
111         public void Add(string name, RouteBase item)
112         {
113             if (item == null)
114             {
115                 throw new ArgumentNullException("item");
116             }
117             if (!string.IsNullOrEmpty(name) && this._namedMap.ContainsKey(name))
118             {
119                 throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("RouteCollection_DuplicateName"), new object[]
120                 {
121                     name
122                 }), "name");
123             }
124             base.Add(item);
125             if (!string.IsNullOrEmpty(name))
126             {
127                 this._namedMap[name] = item;
128             }
129         }
130 
131         public Route MapPageRoute(string routeName, string routeUrl, string physicalFile)
132         {
133             return this.MapPageRoute(routeName, routeUrl, physicalFile, true, null, null, null);
134         }
135 
136         public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess)
137         {
138             return this.MapPageRoute(routeName, routeUrl, physicalFile, checkPhysicalUrlAccess, null, null, null);
139         }
140 
141         public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults)
142         {
143             return this.MapPageRoute(routeName, routeUrl, physicalFile, checkPhysicalUrlAccess, defaults, null, null);
144         }
145 
146         public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints)
147         {
148             return this.MapPageRoute(routeName, routeUrl, physicalFile, checkPhysicalUrlAccess, defaults, constraints, null);
149         }
150 
151         public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens)
152         {
153             if (routeUrl == null)
154             {
155                 throw new ArgumentNullException("routeUrl");
156             }
157             Route route = new Route(routeUrl, defaults, constraints, dataTokens, new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));
158             this.Add(routeName, route);
159             return route;
160         }
161 
162         protected override void ClearItems()
163         {
164             this._namedMap.Clear();
165             base.ClearItems();
166         }
167 
168         public IDisposable GetReadLock()
169         {
170             this._rwLock.EnterReadLock();
171             return new RouteCollection.ReadLockDisposable(this._rwLock);
172         }
173 
174         private RequestContext GetRequestContext(RequestContext requestContext)
175         {
176             if (requestContext != null)
177             {
178                 return requestContext;
179             }
180             HttpContext expr_0A = HttpContext.Current;
181             if (expr_0A == null)
182             {
183                 throw new InvalidOperationException(SR.GetString("RouteCollection_RequiresContext"));
184             }
185             return new RequestContext(new HttpContextWrapper(expr_0A), new RouteData());
186         }
187 
188         private bool IsRouteToExistingFile(HttpContextBase httpContext)
189         {
190             string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
191             return appRelativeCurrentExecutionFilePath != "~/" && this.VPP != null && (this.VPP.FileExists(appRelativeCurrentExecutionFilePath) || this.VPP.DirectoryExists(appRelativeCurrentExecutionFilePath));
192         }
193 
194         public RouteData GetRouteData(HttpContextBase httpContext)
195         {
196             if (httpContext == null)
197             {
198                 throw new ArgumentNullException("httpContext");
199             }
200             if (httpContext.Request == null)
201             {
202                 throw new ArgumentException(SR.GetString("RouteTable_ContextMissingRequest"), "httpContext");
203             }
204             if (base.Count == 0)
205             {
206                 return null;
207             }
208             bool flag = false;
209             bool flag2 = false;
210             if (!this.RouteExistingFiles)
211             {
212                 flag = this.IsRouteToExistingFile(httpContext);
213                 flag2 = true;
214                 if (flag)
215                 {
216                     return null;
217                 }
218             }
219             using (this.GetReadLock())
220             {
221                 foreach (RouteBase current in this)
222                 {
223                     RouteData routeData = current.GetRouteData(httpContext);
224                     if (routeData != null)
225                     {
226                         RouteData result;
227                         if (!current.RouteExistingFiles)
228                         {
229                             if (!flag2)
230                             {
231                                 flag = this.IsRouteToExistingFile(httpContext);
232                             }
233                             if (flag)
234                             {
235                                 result = null;
236                                 return result;
237                             }
238                         }
239                         result = routeData;
240                         return result;
241                     }
242                 }
243             }
244             return null;
245         }
246 
247         private string NormalizeVirtualPath(RequestContext requestContext, string virtualPath)
248         {
249             string text = Util.GetUrlWithApplicationPath(requestContext.HttpContext, virtualPath);
250             if (this.LowercaseUrls || this.AppendTrailingSlash)
251             {
252                 int num = text.IndexOfAny(new char[]
253                 {
254                     '?',
255                     '#'
256                 });
257                 string text2;
258                 string str;
259                 if (num >= 0)
260                 {
261                     text2 = text.Substring(0, num);
262                     str = text.Substring(num);
263                 }
264                 else
265                 {
266                     text2 = text;
267                     str = "";
268                 }
269                 if (this.LowercaseUrls)
270                 {
271                     text2 = text2.ToLowerInvariant();
272                 }
273                 if (this.AppendTrailingSlash && !text2.EndsWith("/"))
274                 {
275                     text2 += "/";
276                 }
277                 text = text2 + str;
278             }
279             return text;
280         }
281 
282         public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
283         {
284             requestContext = this.GetRequestContext(requestContext);
285             using (this.GetReadLock())
286             {
287                 using (IEnumerator<RouteBase> enumerator = base.GetEnumerator())
288                 {
289                     while (enumerator.MoveNext())
290                     {
291                         VirtualPathData virtualPath = enumerator.Current.GetVirtualPath(requestContext, values);
292                         if (virtualPath != null)
293                         {
294                             virtualPath.VirtualPath = this.NormalizeVirtualPath(requestContext, virtualPath.VirtualPath);
295                             return virtualPath;
296                         }
297                     }
298                 }
299             }
300             return null;
301         }
302 
303         public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values)
304         {
305             requestContext = this.GetRequestContext(requestContext);
306             if (string.IsNullOrEmpty(name))
307             {
308                 return this.GetVirtualPath(requestContext, values);
309             }
310             RouteBase routeBase;
311             bool flag;
312             using (this.GetReadLock())
313             {
314                 flag = this._namedMap.TryGetValue(name, out routeBase);
315             }
316             if (!flag)
317             {
318                 throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("RouteCollection_NameNotFound"), new object[]
319                 {
320                     name
321                 }), "name");
322             }
323             VirtualPathData virtualPath = routeBase.GetVirtualPath(requestContext, values);
324             if (virtualPath != null)
325             {
326                 virtualPath.VirtualPath = this.NormalizeVirtualPath(requestContext, virtualPath.VirtualPath);
327                 return virtualPath;
328             }
329             return null;
330         }
331 
332         public IDisposable GetWriteLock()
333         {
334             this._rwLock.EnterWriteLock();
335             return new RouteCollection.WriteLockDisposable(this._rwLock);
336         }
337 
338         public void Ignore(string url)
339         {
340             this.Ignore(url, null);
341         }
342 
343         public void Ignore(string url, object constraints)
344         {
345             if (url == null)
346             {
347                 throw new ArgumentNullException("url");
348             }
349             RouteCollection.IgnoreRouteInternal item = new RouteCollection.IgnoreRouteInternal(url)
350             {
351                 Constraints = new RouteValueDictionary(constraints)
352             };
353             base.Add(item);
354         }
355 
356         protected override void InsertItem(int index, RouteBase item)
357         {
358             if (item == null)
359             {
360                 throw new ArgumentNullException("item");
361             }
362             if (base.Contains(item))
363             {
364                 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString("RouteCollection_DuplicateEntry"), new object[0]), "item");
365             }
366             base.InsertItem(index, item);
367         }
368 
369         protected override void RemoveItem(int index)
370         {
371             this.RemoveRouteName(index);
372             base.RemoveItem(index);
373         }
374 
375         private void RemoveRouteName(int index)
376         {
377             RouteBase routeBase = base[index];
378             foreach (KeyValuePair<string, RouteBase> current in this._namedMap)
379             {
380                 if (current.Value == routeBase)
381                 {
382                     this._namedMap.Remove(current.Key);
383                     break;
384                 }
385             }
386         }
387 
388         protected override void SetItem(int index, RouteBase item)
389         {
390             if (item == null)
391             {
392                 throw new ArgumentNullException("item");
393             }
394             if (base.Contains(item))
395             {
396                 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.GetString("RouteCollection_DuplicateEntry"), new object[0]), "item");
397             }
398             this.RemoveRouteName(index);
399             base.SetItem(index, item);
400         }
401     }

 RouteCollection类型直接继承Collection<RouteBase>,这个关系很明显,他就是用于存放Route路由规则对象的,用于注册Route路由对象的方法就是MapPageRoute方法,基于篇幅,其他方法就不细说了,大家可以细看。

  4、RouteHandler

     到此,我们有了路由规则对象Route,也通过RouteTable的Routes属性注册好了,系统启动了,我们要截获请求,截获请求的类文件就是UrlRoutingModule,在上面我已经贴出该类的全部源码了,这里就不写了,截获请求后,开始和RouteTable里面的每一个Route对象进行比较,如果匹配就获得RouteData对象了,有了RouteData对象,其实我们是为了获得RouteHandler的值,有了他的值我们才可以继续,我说了这么多就是这个方法所实现的:

public virtual void PostResolveRequestCache(HttpContextBase context)
{
            RouteData routeData = this.RouteCollection.GetRouteData(context);
            if (routeData == null)
            {
                return;
            }
            IRouteHandler routeHandler = routeData.RouteHandler;
            if (routeHandler == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
            }
            if (routeHandler is StopRoutingHandler)
            {
                return;
            }
            RequestContext requestContext = new RequestContext(context, routeData);
            context.Request.RequestContext = requestContext;
            IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
            if (httpHandler == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
                {
                    routeHandler.GetType()
                }));
            }
            if (!(httpHandler is UrlAuthFailureHandler))
            {
                context.RemapHandler(httpHandler);
                return;
            }
            if (FormsAuthenticationModule.FormsAuthRequired)
            {
                UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
                return;
            }
            throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
}

有了RouteData对象,在和HttpContext对象一起封装为RequestContext对象,

 
RequestContext requestContext = new RequestContext(context, routeData);
     context.Request.RequestContext = requestContext;

然后我们根据RouteData对象的RouteHandler属性获取HttpHandler,我们调用GetHttpHandler方法的时候我们需要RequestContext类型作为参数,代码如下:
 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

RouoteHandler属性表示的类型必须实现了如下接口:
public interface IRouteHandler
{
        IHttpHandler GetHttpHandler(RequestContext requestContext);
}
 

最后,我们重新路由HttpHandler,在ASP.NET MVC终究是MvcHandler,他开始结果整个请求,进行Controlller激活和Action方法的执行。

context.RemapHandler(httpHandler);

 

四、结论

    好了,做个总结吧,总体来说不是很难,只要大家理顺了,就好了,这些类的设计都是有因果关系的,理解这种因果关系,再把我前后顺序,理解起来就简单了。

   今天就到这里,写的有点长,大家慢慢看,欢迎讨论,我要去赶火车了。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2024 CODEPRJ.COM