深入源码解析类Route

  

  微软官网对这个类的说明是:提供用于定义路由及获取路由相关信息的属性和方法。这个说明已经很简要的说明了这个类的作用,下面我们就从源码的角度来看看这个类的内部是如何工作的。

 1     public class Route : RouteBase {
 2 
 3         private string _url;
 4         private ParsedRoute _parsedRoute;
 5 
 6         public Route(string url, IRouteHandler routeHandler) {
 7             Url = url;
 8             RouteHandler = routeHandler;
 9         }      
10         
11         public RouteValueDictionary Defaults {
12             get;
13             set;
14         }
15 
16         public IRouteHandler RouteHandler {
17             get;
18             set;
19         }
20 
21         public string Url {
22             get {
23                 return _url ?? String.Empty;
24             }
25             set {
26                 _parsedRoute = RouteParser.Parse(value);
27                 _url = value;
28             }
29         }
30 
31         public override RouteData GetRouteData(HttpContextBase httpContext) {
32            
33             string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
34 
35             RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults);
36 
37             if (values == null) {
38                 return null;
39             }
40 
41             RouteData routeData = new RouteData(this, RouteHandler);
42 
43             if (!ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest)) {
44                 return null;
45             }
46             foreach (var value in values) {
47                 routeData.Values.Add(value.Key, value.Value);
48             }
49 
50             if (DataTokens != null) {
51                 foreach (var prop in DataTokens) {
52                     routeData.DataTokens[prop.Key] = prop.Value;
53                 }
54             }
55 
56             return routeData;
57         }
58     }
Route部分源码

  上面是类的核心代码,如果在这里对所有的代码都进行列出或学习,则无法突出重点。

  主要包括构造函数,几个属性,一个方法。我们从这个类的对象被创建的地方开始,逐步深入理解。

   这个方法会在应用程序启动时被第一个调用,在这里,我们注册了全局的路由信息。

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
       //将全局变量 RouteTable.Routes作为参数传递进方法里,然后向这个全局变量添加数据。系统定义的路由数据都被存放在这里变量里  
       RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

 

2从RouteConfig这个类开始,这是创建Mvc项目时,系统自动替我们添加的类,也是很重要的一个类.

 1  public class RouteConfig
 2     {
 3         public static void RegisterRoutes(RouteCollection routes)
 4         {
 5             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 6 
 7             routes.MapRoute(
 8                 name: "Default",
 9                 url: "{controller}/{action}/{id}",
10                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
11             );
12         }
13     }
RouteConfig-开始的地方

  在这里类里,系统替我们添加了默认的URL路由并添加了默认值。

  这里调用了RouteCollection对象的MapRoute方法进行路由的映射,但是查看RouteCollection这个类,并不能找到MapRoute这个方法。

  这个方法的实现,是通过RouteCollection的一个拓展方法进行实现。

 1 public static class RouteCollectionExtensions
 2     {
 3         public static void IgnoreRoute(this RouteCollection routes, string url, object constraints)
 4         {
 5             if (routes == null)
 6             {
 7                 throw new ArgumentNullException("routes");
 8             }
 9             if (url == null)
10             {
11                 throw new ArgumentNullException("url");
12             }
13 
14             IgnoreRouteInternal route = new IgnoreRouteInternal(url)
15             {
16                 Constraints = CreateRouteValueDictionaryUncached(constraints)
17             };
18 
19             ConstraintValidation.Validate(route);
20 
21             routes.Add(route);
22         }
23 
24         public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
25         {
26             if (routes == null)
27             {
28                 throw new ArgumentNullException("routes");
29             }
30             if (url == null)
31             {
32                 throw new ArgumentNullException("url");
33             }
34 
35             Route route = new Route(url, new MvcRouteHandler())
36             {
37                 Defaults = CreateRouteValueDictionaryUncached(defaults),
38                 Constraints = CreateRouteValueDictionaryUncached(constraints),
39                 DataTokens = new RouteValueDictionary()
40             };
41 
42             ConstraintValidation.Validate(route);
43 
44             if ((namespaces != null) && (namespaces.Length > 0))
45             {
46                 route.DataTokens[RouteDataTokenKeys.Namespaces] = namespaces;
47             }
48 
49             routes.Add(name, route);
50 
51             return route;
52         }
53     }
RouteCollectionExtensions部分代码

  在上面的MapRoute方法里,类Route的对象使用指定的值被创建。这也是在系统中,Route对象最初被创建的对象。在这里,路由被定义。

其中最核心的就是下面的代码,我们也将重点讲解下面的代码,看我们的路由信息时如何被定义的以及为什么这样定义。

1             Route route = new Route(url, new MvcRouteHandler())
2             {
3                 Defaults = CreateRouteValueDictionaryUncached(defaults),
4                 Constraints = CreateRouteValueDictionaryUncached(constraints),
5                 DataTokens = new RouteValueDictionary()
6             };

  在第一行中,给Route的构造方法传递了两个参数。

  第一个是url"{controller}/{action}/{id}"这样的值,这样的一个值,定义了系统能处理的路由模板,后面每当有请求被处理,

总是会将请求的http链接与这个路由模板进行匹配,看是否满足我们定义的模板。

  第二个参数则是实现了IRouteHandler接口的类MvcRouteHandler。使用该类对象的主要作用是创建实际处理请求的类MvcHandler对象。

关于这两个类的作用及地位会在其他随笔里介绍,这里仅作了解。

 

下面让我们进入类Route的内部,看构造函数里到底发生了什么。也就是本片文章一开始的那段代码。然后在返回看看对Defaults属性的赋值。

 1   public Route(string url, IRouteHandler routeHandler) {
 2              Url = url;
 3              RouteHandler = routeHandler;
 4        }
 5   public string Url {
 6     get {
 7         return _url ?? String.Empty;
 8        }
 9     set {
10         _parsedRoute = RouteParser.Parse(value);
11         _url = value;
12        }
13   }

  在构造方法里,将传递进去的连个参数保存在两个属性里,而仅在Url属性中,对url路由模板做了特殊处理。所做的这些特殊处理,也是它可以匹配用户的

请求连接的关键。我将第10行使用的代码用源代码中拷贝了出来,新建了一个项目,进行测试。返回的_parsedRoute对象如下图所示。

这里主要使用了两个类,一个是ContentPathSegment,一个是SeparatorPathSegment(代表URL中的“/”分隔符)。重点解释ContentPathSegment类。上截图。

从截图中可以看出,这个类主要存储的是我们定义的url模板切割后子段值。

下面列出该类的具体代码,可以比较理解.

    //代表不是分隔符的网段。 它包含诸如文字和参数的子段。
    internal sealed class ContentPathSegment : PathSegment
    {
        public ContentPathSegment(IList<PathSubsegment> subsegments)
        {
            Subsegments = subsegments;
        }

        public bool IsCatchAll
        {
            get
            {
                // 
                return Subsegments.Any<PathSubsegment>(seg => (seg is ParameterSubsegment) && (((ParameterSubsegment)seg).IsCatchAll));
            }
        }

        public IList<PathSubsegment> Subsegments
        {
            get;
            private set;
        }
    }
类ContentPathSegment

 

至此,Route类构造函数中的结束已经完毕,下面解释另外一个属性的赋值--Defaults。这是属性中存储的是我们给url路由模板指定的默认值,在http请求时,如果只输入了ip,没有指定控制器名称和action时,使用这里的默认值。

 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }

Defaults = CreateRouteValueDictionaryUncached(defaults),

下面是经转化后返回的值的截图,该属性返回的是一个RouteValueDictionary字典。

 

至此,类Route的创建已经完成,该类的创建工作在应用程序启动时完成,且只会被初始化一次,然后保存在路由集合中。

有两个重点,1是路由模板经过处理后得到的_parsedRoute对象;2是传递的路由默认值。这两个对象是Route类的核心功能点。

 

下面,我们就从这个类的使用角度开始解析。UrlRoutingModule这个类是使用Route开始的地方

 1  public class UrlRoutingModule : IHttpModule {
 2         private static readonly object _contextKey = new Object();
 3         private static readonly object _requestDataKey = new Object();
 4         private RouteCollection _routeCollection;
 5 
 6         public RouteCollection RouteCollection {
 7             get {
 8                 if (_routeCollection == null) {
 9                     _routeCollection = RouteTable.Routes;
10                 }
11                 return _routeCollection;
12             }
13             set {
14                 _routeCollection = value;
15             }
16         }
17 
18         protected virtual void Dispose() {
19         }
20 
21         protected virtual void Init(HttpApplication application) {
22 
23             if (application.Context.Items[_contextKey] != null) {
24                 return; // already added to the pipeline
25             }
26             application.Context.Items[_contextKey] = _contextKey;
27             application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
28         }
29 
30         private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) {
31             HttpApplication app = (HttpApplication)sender;
32             HttpContextBase context = new HttpContextWrapper(app.Context);
33             PostResolveRequestCache(context);
34         }
35 
36 
37         public virtual void PostResolveRequestCache(HttpContextBase context) {
38             // Match the incoming URL against the route table
39             RouteData routeData = RouteCollection.GetRouteData(context);
40 
41             // Do nothing if no route found
42             if (routeData == null) {
43                 return;
44             }
45 
46             // If a route was found, get an IHttpHandler from the route's RouteHandler
47             IRouteHandler routeHandler = routeData.RouteHandler;
48             if (routeHandler == null) {
49                 throw new InvalidOperationException(
50                     String.Format(
51                         CultureInfo.CurrentCulture,
52                         SR.GetString(SR.UrlRoutingModule_NoRouteHandler)));
53             }
54 
55             if (routeHandler is StopRoutingHandler) {
56                 return;
57             }
58             RequestContext requestContext = new RequestContext(context, routeData);
59             context.Request.RequestContext = requestContext;
60 
61             IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
62             if (httpHandler == null) {
63                 throw new InvalidOperationException(
64                     String.Format(
65                         CultureInfo.CurrentUICulture,
66                         SR.GetString(SR.UrlRoutingModule_NoHttpHandler),
67                         routeHandler.GetType()));
68             }
69 
70             if (httpHandler is UrlAuthFailureHandler) {
71                 if (FormsAuthenticationModule.FormsAuthRequired) {
72                     UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
73                     return;
74                 }
75                 else {
76                     throw new HttpException(401, SR.GetString(SR.Assess_Denied_Description3));
77                 }
78             }
79 
80             // Remap IIS7 to our handler
81             context.RemapHandler(httpHandler);
82         }
83 
84         #region IHttpModule Members
85         void IHttpModule.Dispose() {
86             Dispose();
87         }
88 
89         void IHttpModule.Init(HttpApplication application) {
90             Init(application);
91         }
92         #endregion
93     }
UrlRoutingModule部分代码

这是一个路由模块类,在网站启动时,会自动从配置文件中加载这个类。在类的Init方法中,会传递一个HttpApplication对象,然后会注册HttpApplication对象的PostResolveRequestCache事件,关于类HttpApplication

会在其他随笔中重点的讲解,这里仅做介绍。我们只需知道,每当一个请求到达时,HttpApplication对象的PostResolveRequestCache事件会被出发,进而调用类UrlRoutingModule的PostResolveRequestCache方法。

在这个方法里,会根据亲求的url链接,去匹配一个系统中已经定义的路由信息。

1 RouteData routeData = RouteCollection.GetRouteData(context);

这是PostResolveRequestCache方法的第一行代码,从路由集合中获取一个路由数据。RouteCollection这个属性的真实值就是RouteTable.Routes,这其中的数据,就是本文上半部分介绍的Route被创建后存储的地方。RouteCollection中存储的就是一个个的Route对象,所以上面的一行代码,最终就是遍历这些Route对象,然后调用每一个对象的GetRouteData方法。

 1         public override RouteData GetRouteData(HttpContextBase httpContext) {
 2             //解析传入的URL(我们修剪掉前两个字符,因为它们总是“〜/”)
 3             //解析后的字符串类似这样 Home/Index、User/Info 这样的格式
 4             string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
 5         //将用户请求的url中的与控制器和Action相关的字符串与我们定义的路由信息进行匹配            
 6        RouteValueDictionary values = _parsedRoute.Match(requestPath, Defaults);
 7        //如果没有匹配到数据,则返回空值           
 8        if (values == null) {
 9                 return null;
10             }
11        //说明请求的url解析成功,则新建一个路由数据对象,且将当前对象及当前对象的一个属性传递给构造函数
12             RouteData routeData = new RouteData(this, RouteHandler);
13             //将匹配到的路由信息添加到路由数据中
14             foreach (var value in values) {
15                 routeData.Values.Add(value.Key, value.Value);
16             }
17             return routeData;
18         }        

根据此RouteData对象可以获取一个实现了IRouteHandler接口的类(MvcRouteHandler)的对象,而该对象又可以获取一个实现了IHttpHanlder的类(MvcHandler)的对象,

该对象,是真正处理用于请求的对象,其他所做的一切都只是准备工作。

 

结束语:

  该类的作用第一是用来存储我们定义的基础路由信息,第二是用来匹配检测用户输入的URL,第三是保存一个实现了IRouteHandler接口的类对象。

 

posted @ 2018-05-02 22:51  来分享_share  阅读(841)  评论(0编辑  收藏  举报