前言
前面的幾個篇幅對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宿主環境中管道、路由
首先我們先來看個示意圖,大概的描述了在SelfHost宿主環境中管道形态。
圖1
<a href="http://s3.51cto.com/wyfs02/M01/44/5A/wKiom1PheNXAw4NlAALM-TfFVAM821.jpg" target="_blank"></a>
因為在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對象。
示例代碼1-1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code> </code><code>publicclassHttpBinding : Binding, IBindingRuntimePreferences</code>
<code> </code><code>{</code>
<code> </code><code>publicHttpBinding()</code>
<code> </code><code>{</code>
<code> </code><code>this</code><code>.Initialize();</code>
<code> </code><code>}</code>
<code> </code><code>privatevoidInitialize()</code>
<code> </code><code>this</code><code>._security=newHttpBindingSecurity();</code>
<code> </code><code>this</code><code>._httpTransportBindingElement=newHttpTransportBindingElement();</code>
<code> </code><code>this</code><code>._httpTransportBindingElement.ManualAddressing=</code><code>true</code><code>;</code>
<code> </code><code>this</code><code>._httpsTransportBindingElement=newHttpsTransportBindingElement();</code>
<code> </code><code>this</code><code>._httpsTransportBindingElement.ManualAddressing=</code><code>true</code><code>;</code>
<code> </code><code>this</code><code>._httpMessageEncodingBindingElement=newHttpMessageEncodingBindingElement();</code>
<code> </code><code>}</code>
<code>}</code>
在示例代碼1-1中我們可以清楚的看到在HttpBinding對象的構造函數中分别的對幾種BindingElement進行了執行個體化指派,我們隻對其中的HttpTransportBindingElement和HttpMessageEncodingBindingElement進行講解也就是圖1中所示的那樣。
HttpTransportBindingElement對象的主要職責就是生成相對應的管道監聽器,這裡對應的就是IChannelListener<TChannel>泛型類型了,而生成好對應的管道監聽器之後,監聽器之後會開始監聽與之對應的管道層,與HttpTransportBindingElement對象以及監聽器對應的也就是TransprotChannel管道層了,它負責請求消息的接收和響應消息的發送。
HttpMessageEncodingBindingElement類型的對象所做操作同HttpTransportBindingElement類型一緻,都是先要生成對應的管道監聽器,在這裡與之對應的就是HttpMessageEncodingChannelListener類型,在監聽器生成好之後也會去監聽對應的EncodingChannel管道層,而EncodingChannel管道層主要的作用就是将請求資訊封裝為HttpMessage類型的消息對象,之後由HttpMessage消息對象進入ASP.NET Web API架構的管道系統中。
上面說的是在請求未到達ASP.NET Web API架構的管道系統中的時候在外部的一些處理和操作,下面我們就要說明一下内部,在上篇的《ASP.NET Web API 管道模型》篇幅中有示例代碼示範過在SelfHost環境下管道的注冊,我們這裡看一下在SelfHost環境中Web API架構自身的管道系統裡的對象的一些類型。
HttpSelfHostServer消息處理程式(實作類-管道頭)System.Web.Http.SelfHost
<code> </code><code>publicsealedclassHttpSelfHostServer : HttpServer</code>
<code> </code><code>publicHttpSelfHostServer(HttpSelfHostConfigurationconfiguration);</code>
<code> </code><code>publicHttpSelfHostServer(HttpSelfHostConfigurationconfiguration, HttpMessageHandlerdispatcher);</code>
<code> </code><code>publicTaskCloseAsync();</code>
<code> </code><code>protectedoverridevoidDispose(booldisposing);</code>
<code> </code><code>publicTaskOpenAsync();</code>
可以看到HttpSelfHostServer類型繼承自HttpServer,在上篇中我們也就提到過HttpServer是繼承自DelegatingHandler抽象類型的消息處理程式基類,DelegatingHandler與HttpMessageHandler的不同之處就是多了個指向下一個處理程式的引用,當然了作為一個管道系統中第一個消息處理程式必須是要有指向下一個處理程式引用的這麼一個辨別,這樣是合理的。我們再看HttpSelfHostServer類型的構造函數的參數類型。
HttpSelfHostConfiguration類型是繼承自HttpConfiguration類型的,在上篇中我們也說過,HttpConfiguration中可以配置管道中的大多數資訊,這個大家可以自己去看一下。在重載構造函數中有了第二個構造函數參數,HttpMessageHandler類型的參數,在預設使用HttpSelfHostServer的時候假使不使用這個重載的構造函數,那麼在HttpSelfHostServer執行個體化的之前先執行個體化之前,其基類HttpServer的構造函數開始執行,是以在看下HttpServer類型的構造函數的時候我們可以看到這裡預設設定的HttpMessageHandler類型的參數到底是什麼樣子的。
17
18
19
20
21
22
23
24
25
26
<code>publicHttpServer() : </code><code>this</code><code>(newHttpConfiguration())</code>
<code> </code>
<code> </code><code>publicHttpServer(HttpMessageHandlerdispatcher) : </code><code>this</code><code>(newHttpConfiguration(), dispatcher)</code>
<code> </code><code>publicHttpServer(HttpConfigurationconfiguration) : </code><code>this</code><code>(configuration, newHttpRoutingDispatcher(configuration))</code>
<code> </code><code>publicHttpServer(HttpConfigurationconfiguration, HttpMessageHandlerdispatcher)</code>
<code> </code><code>this</code><code>._initializationLock=newobject();</code>
<code> </code><code>if</code> <code>(configuration==</code><code>null</code><code>)</code>
<code> </code><code>{</code>
<code> </code><code>throwSystem.Web.Http.Error.ArgumentNull(</code><code>"configuration"</code><code>);</code>
<code> </code><code>}</code>
<code> </code><code>if</code> <code>(dispatcher==</code><code>null</code><code>)</code>
<code> </code><code>throwSystem.Web.Http.Error.ArgumentNull(</code><code>"dispatcher"</code><code>);</code>
<code> </code><code>this</code><code>._dispatcher=dispatcher;</code>
<code> </code><code>this</code><code>._configuration=configuration;</code>
這裡大家可以清楚的看到是HttpRoutingDispatcher類型作為管道的最後一個處理程式的類型,對于這個類型以及詳細的資訊,在下面的路由小節中會有說明。
對于路由的其他知識這裡就不說了,就是簡要的提一下在SelfHost中路由、管道的一些細節。
圖2
<a href="http://s3.51cto.com/wyfs02/M00/44/5A/wKiom1PheQiRQ6ibAAPispqhnxE995.jpg" target="_blank"></a>
這裡要詳細說明的就是HttpRoutingDispatcher類型中的SendAsync()方法,看下源碼中的實作這樣更清楚。
27
28
<code> </code><code>publicclassHttpRoutingDispatcher : HttpMessageHandler</code>
<code> </code><code>//Fields</code>
<code> </code><code>privatereadonlyHttpConfiguration_configuration;</code>
<code> </code><code>privatereadonlyHttpMessageInvoker_defaultInvoker;</code>
<code> </code>
<code> </code><code>//Methods</code>
<code> </code><code>publicHttpRoutingDispatcher(HttpConfigurationconfiguration)</code>
<code> </code><code>: </code><code>this</code><code>(configuration, newHttpControllerDispatcher(configuration))</code>
<code> </code><code>protectedoverrideTask<HttpResponseMessage>SendAsync(HttpRequestMessagerequest, CancellationTokencancellationToken)</code>
<code> </code><code>IHttpRouteDatarouteData;</code>
<code> </code><code>if</code> <code>(!request.Properties.TryGetValue<IHttpRouteData>(HttpPropertyKeys.HttpRouteDataKey, outrouteData))</code>
<code> </code><code>routeData=</code><code>this</code><code>._configuration.Routes.GetRouteData(request);</code>
<code> </code><code>if</code> <code>(routeData==</code><code>null</code><code>)</code>
<code> </code><code>{</code>
<code> </code><code>returnTaskHelpers.FromResult<HttpResponseMessage>(request.CreateErrorResponse(HttpStatusCode.NotFound, Error.Format(SRResources.ResourceNotFound, newobject[] { request.RequestUri }), SRResources.NoRouteData));</code>
<code> </code><code>}</code>
<code> </code><code>request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData);</code>
<code> </code><code>RemoveOptionalRoutingParameters(routeData.Values);</code>
<code> </code><code>HttpMessageInvokerinvoker= (routeData.Route.Handler==</code><code>null</code><code>) ?</code><code>this</code><code>._defaultInvoker : newHttpMessageInvoker(routeData.Route.Handler, </code><code>false</code><code>);</code>
<code> </code><code>returninvoker.SendAsync(request, cancellationToken);</code>
<code> </code><code>}</code>
我們先看一下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控制器有關了,這裡就不提前說了,後面一定會講到。
本文轉自jinyuan0829 51CTO部落格,原文連結:http://blog.51cto.com/jinyuan/1536204,如需轉載請自行聯系原作者