前言
MVC有一套請求處理的機制,當然Web API也有自己的一套消息處理管道,該消息處理管道貫穿始終都是通過HttpMessageHandler來完成。我們知道請求信息存在 RequestMessage 中,而響應信息則存在 ResponseMessage 中,當請求信息進入到管道中,此時HttpMessageHandler會對此進行相應的處理,當執行到控制器上的方法時此時就會進行響應,生成的響應信息HttpResponseMessage就會逆向通過HttpMessageHandler依次進行處理最終返回給客戶端。下面就請求管道中的對象進行詳細敘述。(若有不妥之處,請海涵)。
HttpMessageHandler
此類是所有管道處理中所有對象的基類,也就是說是最重要的一個類,下面我們借助.NET Reflector打開來看看該類的定義
該類是一個抽象類並且其中最重要的兩個方法是 Dispose 和 SendAsyc ,對於Dispose方法的實現,我們查看如下:
public void Dispose() { this.Dispose(1); GC.SuppressFinalize(this); return; } protected virtual void Dispose(bool disposing) { }
就是個非常標准的實現資源回收的方法,但是其並未實現某個資源的回收,下面會用到,先擱置在這里。
對於第二個方法是SendAsync,看到此方法的第一個參數為HttpRequestMessage,我們能想到此方法是用來處理傳遞進來的請求並返回Task<HttpResponMessage>對象來響應信息,至於第二個參數是與線程有關,通知取消某個操作請參看【CancellationToken】。
DelegatingHandler
經過如上描述,對於請求和響應都是通過HttpMessageHandler來實現,這就相當於是首尾相連,但是真正實現其相連的角色而是DelegatingHandler,並且該類並非僅僅是繼承,而且還進行了相應的一些擴展,下面我們來看看該類的定義:
上述重要的兩個方法 Dispose 和 SendAsync 以及 InnerHandler 字段,下面我們一一來看這三者。
第一個是Dispose,上述已經講過在HttpMessageHandler中並未實現對資源的回收,從這里我們可以看出它對其進行了重寫,說明可能是在其繼承類DelegatingHandler中進行了資源回收,只是猜測,我們查看其具體實現就明了了。如下:
protected override void Dispose(bool disposing) { if (disposing == null) { goto Label_0029; } if (this.disposed != null) { goto Label_0029; } this.disposed = 1; if (this.innerHandler == null) { goto Label_0029; } this.innerHandler.Dispose(); Label_0029: base.Dispose(disposing); return; }
很顯然,通過 this.innerHandler.Dispose(); 我們知道是實現了資源回收了的,那這個 InnerHandler 字段到底是干嘛的了,看其返回類型為HttpMessageHandler,說明是獲得了HttpMessageHandler對象的引用,接着就是重寫基類中的SendAsync方法,而調用此方法正是通過屬性InnerHandler來調用。我們說過InnerHandler是獲得對象HttpMessageHandler的引用說的更加明確就是下一個HttpMessageHandler處理程序的引用。
通過以上敘述,DelegatingHandler就是串聯着整個消息處理管道,因為當前DelegatingHandler處理當前請求完成后,下一個則通過委托給HttpMessageHandler而其引用則交給InnerHandler來進行處理,則整個管道都是依托於DelegatingHandler來實現,整個過程可已將其看成是一個委托鏈來完成,同時命名為DelegatingHandler表面意思為委托處理這樣更貼切的表達了整個過程。但是我們也應該注意到DelegatingHandler中的InnerHandler始終引用了下一個請求的處理,因為在管道中的最后一個處理程序即末尾處理程序中沒有下一個處理程序,所以在最后一個處理程序中不會繼承DelegatingHandler,而是繼承於基類HttpMessageHandler。
對於InnerHandler的實現連接下一個HttpMessageHandler似的委托鏈,類似如下:
public IEnumerable<string> GetHandlerChain(DelegatingHandler handler) { yield return handler.GetType().Name; while (null != handler.InnerHandler) { yield return handler.InnerHandler.GetType().Name; handler = handler.InnerHandler as DelegatingHandler; if (null == handler) { break; } } }
HttpServer
上面我們已經講過除了位於末尾的處理程序是基於HttpMessageHandler外,其余的都是基於DelegatingHandler。同時我們也講過在整個管道中由InnerHandler連接着下一個HttpMessageHandler,也就是InnerHandler處於整個管道的中間,但是管道頭和管道尾是哪個對象呢?管道頭就是我們接下來要講的HttpServer對象,既然該類也是處於整個管道中,則也繼承於DelegatingHandler,下面我們來看看該類的定義:
通過上述知HttpServer類確確實實繼承於DelegatingHandler中並且最重要的兩個屬性是 Configuration 和 Dispatcher ,同時這兩個屬性是只讀的,通過查看如下知:
public HttpConfiguration Configuration { get { return this._configuration; } } public HttpMessageHandler Dispatcher { get { return this._dispatcher; } }
HttpServer類中構造函數有幾個,當顯式指定了上述兩個屬性的值則會在相應的構造函數中進行初始化,但是如果未指定兩個屬性的值毫無疑問則會調用默認構造函數,此時則會創建一個HttpConfiguraion作為屬性Configuration的值,通過默認構造函數可得知,如下:
public HttpServer() { this..ctor(new HttpConfiguration()); return; }
此時屬性Dispatcher的值則是一個 HttpRoutingDispatcher 對象,(該類為管道中的尾,下面會講)因為該屬性的返回值為HttpMessageHandler而HttpRoutingDispatcher類繼承於HttpMessageHandler。當HttpServer被創建后,同時此時管道中的頭和尾就相繼被確定了下來。從在Web API的配置文件中得知,一切配置都是基於HttpConfiguration,所以如果想在管道中自定義處理程序也就是繼承於DelegatingHandler,此時進行注冊該自定義處理程序當然也就得經過HttpConfiguration來實現。
HttpRoutingDispatcher
上述也對該類做了一點鋪墊,此類為管道中的尾即管道中最后一個HttpMessageHandler,同時該類的對象是通過HttpSever即管道中的頭的構造函數而生成。此類的定義如下:
public class HttpRoutingDispatcher : HttpMessageHandler { // Fields private readonly HttpConfiguration _configuration; private readonly HttpMessageInvoker _defaultInvoker; // Methods public HttpRoutingDispatcher(HttpConfiguration configuration); public HttpRoutingDispatcher(HttpConfiguration configuration, HttpMessageHandler defaultHandler); private static void RemoveOptionalRoutingParameters(IDictionary<string, object> routeValueDictionary); protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); }
通過上述此類的定義也驗證了作為管道中的尾是繼承HttpMessageHandler而非繼承於DelegatingHandler,因為當Web API請求的URI是直接請求控制器上的方法,當執行完控制器上的方法時就得作出響應,而HttpRoutingDispatcher作為管道中的最后一個HttpMessageHandler,所以此時要找到控制器並激活控制器上的方法然后逆向通過管道作出響應。通過上述構造函數知,當構建HttpRoutingDispatcher對象時需要指定一個HttpConfigutaion對象,通過上述defaultHandler指定的HttpMessageHandler創建的HttpRoutingDispatcher對象就是為了激活控制器以及方法。
上述就大概介紹了Web API中整個消息管道中幾個重要對象:
管道頭(HttpServer)、管道中間(DelegatingHandler中的InnerHandler)、管道尾(HttpRoutingDispatcher)。
下面給出一張整個管道的大概示意圖。【來源:Web API管道】
總結
本節對Web API的核心之一消息處理管道做了一個稍微詳細的介紹,個人覺得對消息管道原理做一個基本的了解是必須的,當需要在手動定制在各個管道時期的請求時才不至於手足無措,希望通過比較詳細的介紹能夠幫助到大家。