深刻源碼解析類Route

  

  微軟官網對這個類的說明是:提供用於定義路由及獲取路由相關信息的屬性和方法。這個說明已經很簡要的說明了這個類的做用,下面咱們就從源碼的角度來看看這個類的內部是如何工做的。app

 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部分源碼

  上面是類的核心代碼,若是在這裏對全部的代碼都進行列出或學習,則沒法突出重點。ide

  主要包括構造函數,幾個屬性,一個方法。咱們從這個類的對象被建立的地方開始,逐步深刻理解。函數

   這個方法會在應用程序啓動時被第一個調用,在這裏,咱們註冊了全局的路由信息。學習

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這個方法。ui

  這個方法的實現,是經過RouteCollection的一個拓展方法進行實現。this

 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對象最初被建立的對象。在這裏,路由被定義。url

其中最核心的就是下面的代碼,咱們也將重點講解下面的代碼,看咱們的路由信息時如何被定義的以及爲何這樣定義。spa

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接口的類對象。

相關文章
相關標籤/搜索