這段時間在讀園子里Artech大神的《ASP.NET MVC5框架揭秘》,慢慢地從底層了解了MVC模式的設計思路。下面是一些閱讀的總結。
傳統的Web Forms應用,URL指向的是具體的物理文件,而ASP.NET MVC應用一般指向的是某個Controller中的某個Action方法。URL與目標Controller/Action之間的映射關系是通過“路由”來實現的。
路由系統中的幾個核心類的描述:
RouteBase
- 一個抽象基類。
public abstract class RouteBase {
//.NET Framwork4.5以下無該屬性(是否對物理文件采取路由) public bool RouteExistingFiles { get; set; }
//獲取路由數據 public abstract RouteData GetRouteData(HttpContextBase httpContext);
//路由解析生成一個完整的路徑 public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values); }
RouteData
- 封裝了有關路由的信息。
- RouteData通過其RouteHandler屬性返回一個RouteHandler對象。
- RouteHandler實現IRouteHandler中的GetHttpHandler(RequestContext requestContext),返回一個具的HttpHandler對象接管http請求。
public class RouteData { public RouteData(); public RouteData(RouteBase route, IRouteHandler routeHandler); public string GetRequiredString(string valueName); public RouteBase Route { get; set; } public IRouteHandler RouteHandler { get; set; } public RouteValueDictionary DataTokens { get; } public RouteValueDictionary Values { get; } }
VirtualPathData
- 表示有關路由和虛擬路徑的信息。
- 執行RouteBase的GetVirtualPath()會進行路由匹配,將路由變量去替換路由模板中的占位符並生成虛擬路徑。
- 此類為虛擬路徑和Route的封裝。
public class VirtualPathData { public VirtualPathData(RouteBase route, string virtualPath); public RouteValueDictionary DataTokens { get; } public RouteBase Route { get; set; } public string VirtualPath { get; set; } }
Route
- 提供用於定義路由及獲取路由相關信息的屬性和方法。
- 路由解析由路由表中具體的某個Route對象來完成。
public class Route : RouteBase { public Route(string url, IRouteHandler routeHandler); public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler); public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler); public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler); //約束(可用正則) public RouteValueDictionary Constraints { get; set; }
//存儲額外的變量,不參與路由解析 public RouteValueDictionary DataTokens { get; set; } public RouteValueDictionary Defaults { get; set; } public IRouteHandler RouteHandler { get; set; } public string Url { get; set; } public override RouteData GetRouteData(HttpContextBase httpContext); public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values); }
RouteTable
- 存儲應用程序的 URL 路由。
- 靜態只讀屬性Routes訪問全局路由表。
public class RouteTable { public static RouteCollection Routes { get; } }
RouteCollection
- 一組Route的集合,用來操作Route。
- 調用RouteCollection的GetRouteData和GetVirtualPath時會遍歷集合中的所有Route。
- 常用的兩個方法。MapPageRoute:注冊路由。Ignore:忽略對應的URL格式。
總結下類之間的關系:
Route對象代表一條實際的路由規則。調用Route對象的兩個路由匹配方法時進行路由解析,返回的RouteData或VirtualPathData是對Route的封裝。RouteTable存儲了Web應用的全局路由信息,即多個Route對象。
路由注冊
- MVC4中默認的路由注冊
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 } ); }
- 書中帶約束的注冊方式
public static void RegisterRoutes(RouteCollection routes) {
//默認值 var defaults = new RouteValueDictionary{ {"areacode","010"},{"days",2}};
//正則約束 var constaints = new RouteValueDictionary { {"areacode",@"0\d{2,3}"},{"days",@"[1-3]{1}"}};
//說明 var dataTokens = new RouteValueDictionary{ {"defaultCitr\y","BeiJing"},{"defaultDays",2}}; routes.MapPageRoute("default","{areacode}/{}days","~/weather.aspx" ,false,defaults,constaints,dataTokens); }
正則約束是其中一個比較簡單的方法,我們還可以通過自定義約束來實現。實現IRouteConstraint的Match方法。
假設現在我們要通過IRouteConstraint來限制IE瀏覽器的訪問。
public class IERouteConstraint : IRouteConstraint { public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { return !(httpContext.Request.UserAgent.Contains("MSIE")); } }
//在原來的約束基礎上修改 var constaints = new RouteValueDictionary { {"areacode",@"0\d{2,3}"},{"days",@"[1-3]{1}"},new IERouteConstraint()};
注:針對自定義路由約束的詳細內容,請移步:http://www.cnblogs.com/xfrog/archive/2010/12/19/1910428.html。此例也選自該博文。
從HTTP請求到路由解析
- URLRoutingModule派生自IHttpModule。通過它注冊HttpApplication的PostResolveRequestCache事件。
- 當一個HttpApplication對象觸發該事件后,URLRoutingModule通過RouteTable的靜態只讀屬性Routea得到全局的路由表的RouteCollection對象,然后根據當前的上下文創建一個HttpContextWrapper對象(派生自HttpContextBase),並將其作為參數調用RouteCollection對象的GetRouteData方法。
- 如果路由匹配成功,會返回一個具體的RouteData對象。
- URLRoutingModule會將HttpContextWrapper傳入RouteData對象中的RouteHandler。調用它的GetHttpHandler方法,得到一個具體的HttpHandler。URLRoutingModule最后調用HttpContextWrapper對象的RemapHandler方法對得到的HttpHandler進行映射。
- 解析完成,針對當前的HTTP請求就由該Handler來接手。
注:本博文大多數內容來自《ASP.NET MVC5框架揭秘》中,只是做內容的梳理和總結。