微軟官網對這個類的說明是:提供用於定義路由及獲取路由相關信息的屬性和方法。這個說明已經很簡要的說明了這個類的做用,下面咱們就從源碼的角度來看看這個類的內部是如何工做的。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 }
上面是類的核心代碼,若是在這裏對全部的代碼都進行列出或學習,則沒法突出重點。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 }
在這裏類裏,系統替咱們添加了默認的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 }
在上面的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; } }
至此,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 }
這是一個路由模塊類,在網站啓動時,會自動從配置文件中加載這個類。在類的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接口的類對象。