ASP.NET Web API 路由對象介紹

ASP.NET Web API 路由對象介紹

前言

在ASP.NET、ASP.NET MVC和ASP.NET Web API這些框架中都會發現有路由的身影,它們的原理都差很少,只不過在不一樣的環境下做了一些微小的修改,這也是根據每一個框架的特性來制定的,今天咱們就來看一看路由的結構,雖然我在MVC系列裏寫過路由的篇幅不過在這裏是Web API 路由對象介紹。web

 

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宿主環境中管道、路由

 

路由系統概念

路由對象的結構

圖1api

路由系統中最重要的部分也就是路由對象了,那咱們首先就來看一下【路由對象】的定義,不論是在ASP.NET、ASP.NET MVC、仍是ASP.NET Web API的路由系統中路由都要有個名稱,其實這個名稱並非路由對象中的而是在註冊路由信息的時候,添加到路由對象集合的時候須要的名稱,這裏也只是看成路由的一部分,這個你們知道就行了。框架

在生成路由對象的時候咱們要給路由賦值URL模板,這也是共同的,也是必須的,至於約束URL模板的條件是能夠根據本身狀況來定義的。在生成的同時框架會給路由對象賦值上【路由請求處理程序】用以做爲銜接路由系統和框架的主體功能部分。ide

 

註冊路由到系統框架中

圖2函數

在路由定義好以後,咱們便會把它註冊到系統框架中。this

 

路由對象的URL匹配

圖3url

在路由對象註冊到系統框架中以後,這個時候若是有外部的請求的到達,這個時候路由系統會讓路由對象集合中每一個路由對象對這個請求進行匹配,就如圖4同樣。spa

圖43d

這個時候就是路由對象所要能作出的行爲就是URL的匹配,根據什麼來匹配?是根據在路由對象實例化的時候定義好的URL模板和條件,拿請求信息的URL和自身定義的URL模板進行匹配,假使沒有匹配成功則會返回Null,這個時候框架則會讓下一個路由對象來進行匹配直到有匹配的成功爲止,若是這個時候匹配成功了路由則會生成一個【路由數據對象】。code

 

路由數據對象也很重要,由於後續的框架功能部分都是使用它的,它也是整個路由系統的結晶,咱們看下圖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/

本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面

相關文章
相關標籤/搜索