標籤:blog http io os ar 使用 for strong sp
前言
在ASP.NET、ASP.NET MVC和ASP.NET Web API這些架構中都會發現有路由的身影,它們的原理都差不多,只不過在不同的環境下作了一些微小的修改,這也是根據每個架構的特性來制定的,今天我們就來看一看路由的結構,雖然我在MVC系列裡寫過路由的篇幅不過在這裡是Web API 路由對象介紹。
ASP.NET Web API路由、管道
- ASP.NET Web API 開篇介紹樣本
- ASP.NET Web API 路由對象介紹
- ASP.NET Web API 管道模型
- ASP.NET Web API selfhost宿主環境中管道、路由
- ASP.NET Web API webhost宿主環境中管道、路由
路由系統概念路由對象的結構
圖1
路由系統中最重要的部分也就是路由對象了,那我們首先就來看一下【路由對象】的定義,不管是在ASP.NET、ASP.NET MVC、還是ASP.NET Web API的路由系統中路由都要有個名稱,其實這個名稱並不是路由對象中的而是在註冊路由資訊的時候,添加到路由對象集合的時候需要的名稱,這裡也只是當作路由的一部分,這個大家知道就好了。
在產生路由對象的時候我們要給路由賦值URL模板,這也是共同的,也是必須的,至於約束URL模板的條件是可以根據自己情況來定義的。在產生的同時架構會給路由對象賦值上【路由請求處理常式】用以作為銜接路由系統和架構的主體功能部分。
註冊路由到系統架構中
圖2
在路由定義好之後,我們便會把它註冊到系統架構中。
路由對象的URL匹配
圖3
在路由對象註冊到系統架構中之後,這個時候如果有外部的請求的到達,這個時候路由系統會讓路由對象集合中每個路由對象對這個請求進行匹配,就4一樣。
圖4
這個時候就是路由對象所要能做出的行為就是URL的匹配,根據什麼來匹配?是根據在路由對象執行個體化的時候定義好的URL模板和條件,拿請求資訊的URL和自身定義的URL模板進行匹配,假使沒有匹配成功則會返回Null,這個時候架構則會讓下一個路由對象來進行匹配直到有匹配的成功為止,如果這個時候匹配成功了路由則會產生一個【路由資料對象】。
路由資料對象也很重要,因為後續的架構功能部分都是使用它的,它也是整個路由系統的結晶,我們看5
圖5
路由資料對象會保持一個產生它的路由對象的引用,然後是Values的是儲存著路由對象在經過URL匹配後的值,分別表示著URL片段的名字和對應的URL真實值,而DataTokens則是在路由對象定義產生的時候直接帶過來的值,當然了路由請求處理常式也是由執行產生的路由對象帶來的。
在ASP.NET、ASP.NET MVC、ASP.NET Web API這些架構中路由系統都是遵循著上面的所述的這樣一個過程,只不過在不同的架構環境下使用的類型不同,做的處理也不太一樣,但是整體的流程是一致的,下面附6說明了之間的類型的差異性,還有更多的細節就不一一展示了。
圖6
還有在Web API(WebHost)環境下路由顯示的是這樣實質的本質其實又是ASP.NET的路由系統在支援的,這個會在後面的Web API系列篇幅中講解。
下面簡單的示範一下在各種架構環境下的路由對象註冊,
ASP.NET:
RouteTable.Routes.MapPageRoute( "ASP.NETRoute", "ProductInfo/{action}/{id}", "~/ProductInfo.aspx", true, new RouteValueDictionary { { "id", RouteParameter.Optional }, { "action", "show" } } );
ASP.NET MVC:
RouteTable.Routes.MapRoute( "ASP.NETMVCRoute", "ProductInfo/{action}/{id}", new { controller="Product",action="show",id=RouteParameter.Optional} );
ASP.NET Web API(WEBHOST):
GlobalConfiguration.Configuration.Routes.MapHttpRoute( "WebAPIRoute", "api/{controller}/{id}", new { id = RouteParameter.Optional } );
ASP.NET Web API(SELFHOST):
HttpSelfHostConfiguration configuration = new HttpSelfHostConfiguration("http://loacalhost/selfhost"); using (HttpSelfHostServer selfHostServer = new HttpSelfHostServer(configuration)) { selfHostServer.Configuration.Routes.MapHttpRoute( "DefaultApi", "api/{controller}/{id}", new { id=RouteParameter.Optional}); selfHostServer.OpenAsync(); Console.Read(); }
ASP.NET Web API
路由系列對象
從的圖表中就可以看出,ASP.NET Web API架構在不同的宿主環境下路由系統中所對應的物件類型是不同的,這裡就先給大家介紹在SelfHost環境下的路由系統中的路由對象吧。
SelfHost
宿主環境
Web API路由對象(System.Web.Http.Routing)
HttpRoute
// 摘要: // 表示自承載(即在 ASP.NET 之外承載)的路由類。 public class HttpRoute : IHttpRoute { public HttpRoute(string routeTemplate, HttpRouteValueDictionary defaults, HttpRouteValueDictionary constraints, HttpRouteValueDictionary dataTokens, HttpMessageHandler handler); public IDictionary<string, object> Constraints { get; } public IDictionary<string, object> DataTokens { get; } public IDictionary<string, object> Defaults { get; } public HttpMessageHandler Handler { get; } public string RouteTemplate { get; } public virtual IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request); public virtual IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values); protected virtual bool ProcessConstraint(HttpRequestMessage request, object constraint, string parameterName, HttpRouteValueDictionary values, HttpRouteDirection routeDirection); }
可以從上面的定義中看到HttpRoute對象就是代表著在Web API架構中的路由對象了,在HttpRoute類型定義的建構函式中的參數分別表示著路由模板、路由模板對應的預設值、路由匹配條件、註冊的路由附帶的值以及最後的Http請求處理常式,這幾個參數值也分別對應著HttpRoute類型中的幾個屬性,這個自行看一下就明白了。
Web API路由對象集合(System.Web.Http)
HttpRouteCollection
HttpRouteCollectionExtensions
我們先來看一下HttpRouteCollection類型的擴充類型HttpRouteCollectionExtensions吧
public static class HttpRouteCollectionExtensions { public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate); public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults); public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints); public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler); }
這裡大家可以對比上面的路由註冊時的代碼,就可以知道我們在路由集合 添加/註冊 路由的時候是由HttpRouteCollectionExtensions類型的擴充方法來進行操作的,這個時候我們再看一下方法參數最多的那個MapHttpRoute()方法的實現:
public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler) { if (routes == null) { throw System.Web.Http.Error.ArgumentNull("routes"); } HttpRouteValueDictionary dictionary = new HttpRouteValueDictionary(defaults); HttpRouteValueDictionary dictionary2 = new HttpRouteValueDictionary(constraints); IDictionary<string, object> dataTokens = null; HttpMessageHandler handler2 = handler; IHttpRoute route = routes.CreateRoute(routeTemplate, dictionary, dictionary2, dataTokens, handler2); routes.Add(name, route); return route; }
這裡大家就可以看到了,HttpRoute對象的建立操作和添加操作是在這擴充方法裡執行的,現在我們就可以去看一下HttpRouteCollection類型的定義了,看一下如何建立的IHttpRoute對象:
public class HttpRouteCollection : ICollection<IHttpRoute>, IEnumerable<IHttpRoute>, IEnumerable, IDisposable { public HttpRouteCollection(); public HttpRouteCollection(string virtualPathRoot); public virtual int Count { get; } public virtual bool IsReadOnly { get; } public virtual string VirtualPathRoot { get; } public virtual void Add(string name, IHttpRoute route); public IHttpRoute CreateRoute(string routeTemplate, object defaults, object constraints); public IHttpRoute CreateRoute(string routeTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens); public virtual IHttpRoute CreateRoute(string routeTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler); public virtual IHttpRouteData GetRouteData(HttpRequestMessage request); }
這裡只是其中的一部分,下面我們就來看一下具體的實現,其實就是執行個體化一個HttpRoute路由對象根據使用者配置的參數資訊:
public virtual IHttpRoute CreateRoute(string routeTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler) { HttpRouteValueDictionary dictionary = new HttpRouteValueDictionary(defaults); HttpRouteValueDictionary dictionary2 = new HttpRouteValueDictionary(constraints); return new HttpRoute(routeTemplate, dictionary, dictionary2, new HttpRouteValueDictionary(dataTokens), handler); }
這是路由對象集合類型的第一個作用就是添加/註冊 路由資訊,那麼第二個呢?就是根據請求資訊來匹配路由對象,上面也說過了,其實真正根據請求來匹配的並不是路由對象集合類型(HttpRouteCollection),而是在其中的每個路由,我們看一下HttpRouteCollection的障眼法:
public virtual IHttpRouteData GetRouteData(HttpRequestMessage request) { if (request == null) { throw System.Web.Http.Error.ArgumentNull("request"); } foreach (IHttpRoute route in this._collection) { IHttpRouteData routeData = route.GetRouteData(this._virtualPathRoot, request); if (routeData != null) { return routeData; } } return null; }
從這裡可以看出在路由匹配完成後會返回一個實現IHttpRouteDatarouteData介面的對象,也就是上面所說的路由資料對象。
Web API路由資料對象(System.Web.Http.Routing)
HttpRouteData
public class HttpRouteData : IHttpRouteData { public HttpRouteData(IHttpRoute route); public HttpRouteData(IHttpRoute route, HttpRouteValueDictionary values); public IHttpRoute Route { get; } public IDictionary<string, object> Values { get; } }
其實這裡都不用講了,上面都講過了,HttpRouteData對象包含著產生它的路由對象(HttpRoute)的引用,並且Values值就是經過匹配過後的路由模板值,key鍵對應著Url模板的片段值,value對應著的是片段對應的真實值。
SelfHost環境下的路由就說到這裡,大家看一下如下的,簡單的表示了在SelfHost環境下路由的一個處理過程,具體的細節會在後面的篇幅講解。
圖7
WebHost
宿主環境
Web API路由對象(System.Web.Http.WebHost.Routing)
HostedHttpRoute
internal class HostedHttpRoute : IHttpRoute { // Methods public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler); public IHttpRouteData GetRouteData(string rootVirtualPath, HttpRequestMessage request); public IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values); // Properties public IDictionary<string, object> Constraints { get; } public IDictionary<string, object> DataTokens { get; } public IDictionary<string, object> Defaults { get; } public HttpMessageHandler Handler { get; private set; } internal Route OriginalRoute { get; private set; } public string RouteTemplate { get; }}
從上面的代碼定義中可以看到HostedHttpRoute是程式集內部類型,並且是直接繼承自IHttpRoute介面,跟SelfHost環境中的HttpRoute對象是一點關係都沒有。
從它定義的內部結構來看它跟HttpRoute對象的結構相似,還是那些屬性那些個對象,唯一不同的就是多了個OriginalRoute的唯讀屬性(對於外部來說),這個屬性也就是封裝的HttpWebRoute對象,看下封裝時的實現
public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler) { RouteValueDictionary dictionary = (defaults != null) ? new RouteValueDictionary(defaults) : null; RouteValueDictionary dictionary2 = (constraints != null) ? new RouteValueDictionary(constraints) : null; RouteValueDictionary dictionary3 = (dataTokens != null) ? new RouteValueDictionary(dataTokens) : null; this.OriginalRoute = new HttpWebRoute(uriTemplate, dictionary, dictionary2, dictionary3, HttpControllerRouteHandler.Instance, this); this.Handler = handler; }
在HostedHttpRoute物件建構函數中可以清楚的看到OriginalRoute屬性是賦值的HttpWebRoute對象的執行個體,我們現在就來看一下HttpWebRoute對象的定義:
internal class HttpWebRoute : Route { // Fields internal const string HttpRouteKey = "httproute"; // Methods public HttpWebRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler, IHttpRoute httpRoute); private static HttpRouteDirection ConvertRouteDirection(RouteDirection routeDirection); private static RouteValueDictionary GetRouteDictionaryWithoutHttpRouteKey(IDictionary<string, object> routeValues); public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values); protected override bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection); // Properties public IHttpRoute HttpRoute { get; private set; }}
從這裡可以看到HttpWebRoute對象繼承自ASP.NET中的Route對象,現在就可以理解為HostedHttpRoute對象持有對ASP.NET中Route對象的引用,而在HostedHttpRoute的建構函式實現中,對OriginalRoute屬性是賦值執行個體化的時候,在最後傳入了一個HttpControllerRouteHandler類型的路由處理常式,實則是給ASP.NET中的Route對象的路由處理常式(Routehandler屬性)進行的賦值。這裡路由的具體的操作後續篇幅中會有講到一個全面的過程。
Web API路由對象集合(System.Web.Http.WebHost.Routing
HostedHttpRouteCollection
internal class HostedHttpRouteCollection : HttpRouteCollection { // Fields private readonly RouteCollection _routeCollection; // Methods public HostedHttpRouteCollection(RouteCollection routeCollection); public override void Add(string name, IHttpRoute route); public override void Clear(); public override bool Contains(IHttpRoute item); public override bool ContainsKey(string name); public override void CopyTo(IHttpRoute[] array, int arrayIndex); public override void CopyTo(KeyValuePair<string, IHttpRoute>[] array, int arrayIndex); public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler); public override IEnumerator<IHttpRoute> GetEnumerator(); public override IHttpRouteData GetRouteData(HttpRequestMessage request); public override IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, string name, IDictionary<string, object> values); public override void Insert(int index, string name, IHttpRoute value); private static NotSupportedException NotSupportedByHostedRouteCollection(); private static NotSupportedException NotSupportedByRouteCollection(); public override bool Remove(string name); public override bool TryGetValue(string name, out IHttpRoute route); // Properties public override int Count { get; } public override IHttpRoute this[string name] { get; } public override IHttpRoute this[int index] { get; } public override string VirtualPathRoot { get; } }
看到這裡的代碼定義,HostedHttpRouteCollection對象同樣也是程式集內部類型,繼承自ASP.NET中的RouteCollection對象,這裡要說是CreateRoute()方法和GetRouteData()方法返回的分別是HostedHttpRoute對象和HostedHttpRouteData對象,其實在GetRouteData()方法中起初產生的就是Routedata對象,只不過在返回的時候經過HostedHttpRouteData對象封裝了一下。
Web API路由資料對象(System.Web.Http.WebHost.Routing)
HostedHttpRouteData
這裡我們看一下HostedHttpRouteData類型的定義:
internal class HostedHttpRouteData : IHttpRouteData { // Methods public HostedHttpRouteData(RouteData routeData); // Properties internal RouteData OriginalRouteData { get; private set; } public IHttpRoute Route { get; private set; } public IDictionary<string, object> Values { get; } }
從建構函式的定義就可以看出來是HostedHttpRouteData是封裝的RouteData對象,這些路由流程細節後面篇幅中會有講解。
最後我們看一下在WebHost宿主環境下的路由。
圖8
ASP.NET Web API 路由對象介紹