前面的幾個篇幅對Web API中的路由和管道進行了簡單的介紹並無詳細的去說明一些什麼,然而ASP.NET Web API這個框架因爲宿主環境的不一樣在不一樣的宿主環境中管道中的實現機制和路由的處理方式有着很大的不一樣,因此我會將對應不一樣的宿主環境來分別的作出簡單的講解。web
首先咱們先來看個示意圖,大概的描述了在SelfHost宿主環境中管道形態。框架
圖1ide
由於在WebHost宿主環境中ASP.NET Web API的管道請求接收以及響應的返回最後都是由ASP.NET來包辦的(下一篇中講解),而在SelfHost宿主環境中就苦逼了,沒有那麼簡單了。函數
咱們按照圖1中示意的來說解,首先在SelfHost宿主環境中的項目啓動以後(固然項目要使用Web API框架的),會有一個HttpBinding對象(System.Web.Http.SelfHost.Channels),那這個HttpBinding類型的對象是幹嗎的呢?Httpbinding對象對應着一些個BindingElement對象,而這些BindingElement又各自生成對應的管道層監聽器,這樣就如圖1中所示的那樣,如今咱們看一下以下的示例代碼,看看HttpBinding到底對應着哪些BindingElement對象。this
示例代碼1-1spa
public class HttpBinding : Binding, IBindingRuntimePreferences { public HttpBinding() { this.Initialize(); } private void Initialize() { this._security = new HttpBindingSecurity(); this._httpTransportBindingElement = new HttpTransportBindingElement(); this._httpTransportBindingElement.ManualAddressing = true; this._httpsTransportBindingElement = new HttpsTransportBindingElement(); this._httpsTransportBindingElement.ManualAddressing = true; this._httpMessageEncodingBindingElement = new HttpMessageEncodingBindingElement(); } }
在示例代碼1-1中咱們能夠清楚的看到在HttpBinding對象的構造函數中分別的對幾種BindingElement進行了實例化賦值,咱們只對其中的HttpTransportBindingElement和HttpMessageEncodingBindingElement進行講解也就是圖1中所示的那樣。code
HttpTransportBindingElement對象的主要職責就是生成相對應的管道監聽器,這裏對應的就是IChannelListener<TChannel>泛型類型了,而生成好對應的管道監聽器以後,監聽器以後會開始監聽與之對應的管道層,與HttpTransportBindingElement對象以及監聽器對應的也就是TransprotChannel管道層了,它負責請求消息的接收和響應消息的發送。orm
HttpMessageEncodingBindingElement類型的對象所作操做同HttpTransportBindingElement類型一致,都是先要生成對應的管道監聽器,在這裏與之對應的就是HttpMessageEncodingChannelListener類型,在監聽器生成好以後也會去監聽對應的EncodingChannel管道層,而EncodingChannel管道層主要的做用就是將請求信息封裝爲HttpMessage類型的消息對象,以後由HttpMessage消息對象進入ASP.NET Web API框架的管道系統中。對象
上面說的是在請求未到達ASP.NET Web API框架的管道系統中的時候在外部的一些處理和操做,下面咱們就要說明一下內部,在上篇的《ASP.NET Web API 管道模型》篇幅中有示例代碼演示過在SelfHost環境下管道的註冊,咱們這裏看一下在SelfHost環境中Web API框架自身的管道系統裏的對象的一些類型。blog
HttpSelfHostServer消息處理程序(實現類-管道頭)System.Web.Http.SelfHost
public sealed class HttpSelfHostServer : HttpServer { public HttpSelfHostServer(HttpSelfHostConfiguration configuration); public HttpSelfHostServer(HttpSelfHostConfiguration configuration, HttpMessageHandler dispatcher); public Task CloseAsync(); protected override void Dispose(bool disposing); public Task OpenAsync(); }
能夠看到HttpSelfHostServer類型繼承自HttpServer,在上篇中咱們也就提到過HttpServer是繼承自DelegatingHandler抽象類型的消息處理程序基類,DelegatingHandler與HttpMessageHandler的不一樣之處就是多了個指向下一個處理程序的引用,固然了做爲一個管道系統中第一個消息處理程序必須是要有指向下一個處理程序引用的這麼一個標識,這樣是合理的。咱們再看HttpSelfHostServer類型的構造函數的參數類型。
HttpSelfHostConfiguration類型是繼承自HttpConfiguration類型的,在上篇中咱們也說過,HttpConfiguration中能夠配置管道中的大多數信息,這個你們能夠本身去看一下。在重載構造函數中有了第二個構造函數參數,HttpMessageHandler類型的參數,在默認使用HttpSelfHostServer的時候假使不使用這個重載的構造函數,那麼在HttpSelfHostServer實例化的以前先實例化以前,其基類HttpServer的構造函數開始執行,因此在看下HttpServer類型的構造函數的時候咱們能夠看到這裏默認設置的HttpMessageHandler類型的參數究竟是什麼樣子的。
public HttpServer() : this(new HttpConfiguration()) { } public HttpServer(HttpMessageHandler dispatcher) : this(new HttpConfiguration(), dispatcher) { } public HttpServer(HttpConfiguration configuration) : this(configuration, new HttpRoutingDispatcher(configuration)) { } public HttpServer(HttpConfiguration configuration, HttpMessageHandler dispatcher) { this._initializationLock = new object(); if (configuration == null) { throw System.Web.Http.Error.ArgumentNull("configuration"); } if (dispatcher == null) { throw System.Web.Http.Error.ArgumentNull("dispatcher"); } this._dispatcher = dispatcher; this._configuration = configuration; }
這裏你們能夠清楚的看到是HttpRoutingDispatcher類型做爲管道的最後一個處理程序的類型,對於這個類型以及詳細的信息,在下面的路由小節中會有說明。
對於路由的其餘知識這裏就不說了,就是簡要的提一下在SelfHost中路由、管道的一些細節。
圖2
這裏要詳細說明的就是HttpRoutingDispatcher類型中的SendAsync()方法,看下源碼中的實現這樣更清楚。
public class HttpRoutingDispatcher : HttpMessageHandler { // Fields private readonly HttpConfiguration _configuration; private readonly HttpMessageInvoker _defaultInvoker; // Methods public HttpRoutingDispatcher(HttpConfiguration configuration) : this(configuration, new HttpControllerDispatcher(configuration)) { } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { IHttpRouteData routeData; if (!request.Properties.TryGetValue<IHttpRouteData>(HttpPropertyKeys.HttpRouteDataKey, out routeData)) { routeData = this._configuration.Routes.GetRouteData(request); if (routeData == null) { return TaskHelpers.FromResult<HttpResponseMessage>(request.CreateErrorResponse(HttpStatusCode.NotFound, Error.Format(SRResources.ResourceNotFound, new object[] { request.RequestUri }), SRResources.NoRouteData)); } request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData); } RemoveOptionalRoutingParameters(routeData.Values); HttpMessageInvoker invoker = (routeData.Route.Handler == null) ? this._defaultInvoker : new HttpMessageInvoker(routeData.Route.Handler, false); return invoker.SendAsync(request, cancellationToken); } }
咱們先看一下HttpRoutingDispatcher類型中構造函數,能夠看到在定義的構造函數後面緊接着又在實例基類的構造函數,而且第二個HttpMessageHandler類型的參數爲HttpControllerDispatcher實例的對象,這個咱們先記住就好了。
下面咱們仍是回到HttpRoutingDispatcher類型的SendAsync()方法中,首先咱們會看到從HttpRequestMessage對象實例的Properties屬性集合中獲取路由數據對象。
在SelfHost的環境下這是獲取不到的,爲啥?由於上面以及以前的篇幅中在管道的處理中沒有提到過處理路由而且生成路由數據的。因此這個時候會根據HttpConfiguration中的HttpRouteCollection類型的屬性Routes,Routes屬性再根據SendAsync()方法的參數類型爲HttpRequestMessage的request信息獲取路由數據對象IHttpRouteData。
在匹配成功獲取到路由數據對象(IHttpRouteData)以後便會添加至HttpRequestMessage對象實例(request)的Properties屬性集合中。
以前對於路由的瞭解,最後的執行的Handler都是起初定義在路由對象中的,而在實際狀況中,咱們註冊路由的時候並無,假使這種狀況就在如今發生,能夠看到routeData.Route.Hander==null這個是成立的,因此執行的是咱們先前說過的在構造函數中的HttpControllerDispatcher類型的實例的SendAsync()方法(實際當中HttpControllerDispatcher類型被HttpMessageInvoker類型所封裝)。
而HttpControllerDispatcher類型就跟Web API控制器有關了,這裏就不提早說了,後面必定會講到。
做者:金源
出處:http://www.cnblogs.com/jin-yuan/
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面