RouteTable翻譯過來的意思就是路由表,一個Web應用程序具備一個全局的路由表,該路由表經過System.Web.Routiing.RouteTable的靜態只讀屬性Routes表示,該類型返回一個類型爲System.Web.Routingg.RouteCollection的集合。ide
RouteTable類十分的簡單,以下所示this
public class RouteTable { private static RouteCollection _instance = new RouteCollection(); //返回一個靜態只讀的RouteCollection類型實例 public static RouteCollection Routes { get { return RouteTable._instance; } } public RouteTable() { } }
如今咱們來看一下運行時的狀態url
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); //斷點1 } }
下圖就是當斷點處於斷點1時,RouteTable的狀態 ,咱們在這裏能夠很清楚的看到如今Routes屬性所包含的路由條數爲0,不急咱們繼續向下走。spa
看到名稱就不難猜到,這個應該是表示路由集合,咱們先來看看這個類裏面有什麼新奇玩意。翻譯
public class RouteCollection : Collection<RouteBase> { //其他省略 //是否添加首尾斜槓.默認值爲 false. public bool AppendTrailingSlash { get; set; } //是否將 URL 轉換爲小寫.默認值爲 false. public bool LowercaseUrls { get; set; } //是否應處理與現有文件匹配的 URL.默認值爲 false. public bool RouteExistingFiles { get; set; } //獲取路由信息 public RouteData GetRouteData(HttpContextBase httpContext); //獲取虛擬路徑信息 public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values); //忽略路由URL和相關約束 public void Ignore(string url, object constraints); //添加路由 public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens); }
稍微瞭解了這個類是用來幹什麼的,那麼咱們就要接着上面的程序向下走了,固然先介紹如下RouteBase和Route類吧3d
在上圖中咱們看到了RouteBase,Route類,來講一下它們是什麼吧。code
RouteBase是Route類的父類,咱們仍是來看下它的類結構吧orm
public abstract class RouteBase { private bool _routeExistingFiles = true; // 指示 ASP.NET 路由操做是否應處理與物理文件匹配的 URL,這裏默認是True,便可以使用WebForm方式請求物理文件,可是在MSDN中描述 //這個屬性的默認值爲False public bool RouteExistingFiles { get { return this._routeExistingFiles; } set { this._routeExistingFiles = value; } } // 獲取路由信息,保存在RouteData中 public abstract RouteData GetRouteData(HttpContextBase httpContext); // 獲取虛擬路徑信息,保存在VirtualPathData中 public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values); }
RouteURL模版模式的路由匹配規則就定義在Route中,看下類結構吧對象
public class Route : RouteBase { // 省略代碼..... private string _url; private ParsedRoute _parsedRoute; private const string HttpMethodParameterName = "httpMethod"; // 存儲路由約束 public RouteValueDictionary Constraints { get; set; } // 存儲額外變量,但不會參與針對請求地址的匹配工做,好比Namespaces public RouteValueDictionary DataTokens { get; set; } // 存儲爲路由變量定義的默認值 public RouteValueDictionary Defaults { get; set; } // 路由處理對象 public IRouteHandler RouteHandler { get; set; } // URL模版 public string Url { get { return this._url ?? string.Empty; } set { this._parsedRoute = RouteParser.Parse(value); this._url = value; } } public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) { this.Url = url; this.Defaults = defaults; this.Constraints = constraints; this.DataTokens = dataTokens; this.RouteHandler = routeHandler; } // 重寫父類方法 public override RouteData GetRouteData(HttpContextBase httpContext) { string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo; RouteValueDictionary routeValueDictionary = this._parsedRoute.Match(virtualPath, this.Defaults); if (routeValueDictionary == null) { return null; } RouteData routeData = new RouteData(this, this.RouteHandler); if (!this.ProcessConstraints(httpContext, routeValueDictionary, RouteDirection.IncomingRequest)) { return null; } foreach (KeyValuePair<string, object> current in routeValueDictionary) { routeData.Values.Add(current.Key, current.Value); } if (this.DataTokens != null) { foreach (KeyValuePair<string, object> current2 in this.DataTokens) { routeData.DataTokens[current2.Key] = current2.Value; } } return routeData; } // 重寫父類方法 public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { BoundUrl boundUrl = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints); if (boundUrl == null) { return null; } if (!this.ProcessConstraints(requestContext.HttpContext, boundUrl.Values, RouteDirection.UrlGeneration)) { return null; } VirtualPathData virtualPathData = new VirtualPathData(this, boundUrl.Url); if (this.DataTokens != null) { foreach (KeyValuePair<string, object> current in this.DataTokens) { virtualPathData.DataTokens[current.Key] = current.Value; } } return virtualPathData; } // 驗證參數值是否與該參數的約束匹配 protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { IRouteConstraint routeConstraint = constraint as IRouteConstraint; if (routeConstraint != null) { return routeConstraint.Match(httpContext, this, parameterName, values, routeDirection); } string text = constraint as string; if (text == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[] { parameterName, this.Url })); } object value; values.TryGetValue(parameterName, out value); string input = Convert.ToString(value, CultureInfo.InvariantCulture); string pattern = "^(" + text + ")$"; return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } }
介紹完RouteBase和Route類後,咱們的代碼繼續向下走blog
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, constraints: new { controller = "^H.*" }, namespaces: new[] { "SimpleMVC" } ); }
看上面一段代碼,咱們發現RouteCollection實例有兩個方法,可是System.Web.Routing.RouteCollection類中並無這兩個方法,那這個怎麼實現的呢?
咱們在IgnoreRoute上轉到定義看下,發現咱們跳轉到了System.Web.Mvc.RouteCollectionExtensions這個路由集合擴展類了,在看下這個方法
public static void IgnoreRoute(this RouteCollection routes, string url) { routes.IgnoreRoute(url, null); }
一看恍然大悟,原來是經過擴展方法,感嘆下擴展方法原來是能夠這麼用的。
好了,那麼routes.MapRoute也確定是經過擴展方法注入的。那咱們就看下route.MaoRoute是實現的。
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } // MvcRouteHandler 是請求進入時使用MVC路由關鍵 Route route = new Route(url, new MvcRouteHandler()) { // 存儲爲路由變量定義的默認值 Defaults = RouteCollectionExtensions.CreateRouteValueDictionaryUncached(defaults), // 存儲路由約束 Constraints = RouteCollectionExtensions.CreateRouteValueDictionaryUncached(constraints), // 存儲額外變量,但不會參與針對請求地址的匹配工做,好比Namespaces DataTokens = new RouteValueDictionary() }; ConstraintValidation.Validate(route); if (namespaces != null && namespaces.Length > 0) { route.DataTokens["Namespaces"] = namespaces; } // 向RouteCollection中添加路由 routes.Add(name, route); // 返回該路由 return route; }
好了,咱們大概已經瞭解這兩個擴展方法的做用了,下面咱們來看看它們在運行時的狀態
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }, constraints: new { controller = "^H.*" }, namespaces: new[] { "SimpleMVC" } );// 斷點2 }
咱們看看當斷點停留在斷點2處時,類裏面的狀態是怎樣的?以下圖
咱們能夠很清楚的看到RouteCollection實例包含兩條由規則,這兩條路由規則都是繼承自System.Web.Routing.RouteBase,第一條是咱們定義爲忽略的路由,類型是System.Web.Mvc.RouteCollectionExtensions.IgnoreRouteInternal,該類型繼承子System.Web.Routing.Route,第二條則是咱們定義的有效的路由,類型是System.Web.Routing.Route。
經過上圖,能夠很是明顯的看出,哪些數據存儲到了哪些屬性裏面,能夠有個直觀的理解。
好了,咱們的程序須要繼續向下走,執行完RegisterRoutes方法後,咱們又回到了Application_Start方法。
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); // 斷點1 } // 斷點3
咱們在斷點3出看下各種的狀態,總結下RouteTable,RouteCollection,RouteBase,Route4個類之間的關係,以下圖所示
下面是一張RouteTable,RouteCollection,RouteBase,Route4個類關係圖