ASP.NET Web API 路由對象介紹
前言
在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
作者:金源
出處:http://www.cnblogs.com/jin-yuan/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面