ASP.NET Web API的核心框架是一個(gè)消息處理管道,這個(gè)管道是一組HttpMessageHandler的有序組合。這是一個(gè)雙工管道,請(qǐng)求消息從一端流入并依次經(jīng)過(guò)所有HttpMessageHandler的處理。在另一端,目標(biāo)HttpController被激活,Action方法被執(zhí)行,響應(yīng)消息隨之被生成。響應(yīng)消息逆向流入此管道,同樣會(huì)經(jīng)過(guò)逐個(gè)HttpMessageHandler的處理。這是一個(gè)獨(dú)立于寄宿環(huán)境的抽象管道,如何實(shí)現(xiàn)對(duì)請(qǐng)求的監(jiān)聽(tīng)與接收,以及將接收的請(qǐng)求傳入消息處理管道進(jìn)行處理并將管道生成的響應(yīng)通過(guò)網(wǎng)絡(luò)回傳給客戶端,這就是Web API寄宿需要解決的問(wèn)題。
目錄
一、HttpMessageHandler
二、DelegatingHandler
三、HttpServer
四、HttpRoutingDispatcher
五、HttpControllerDispatcher
一、HttpMessageHandler
ASP.NET Web API的消息處理管道由一組HttpMessageHandler經(jīng)過(guò)“首尾相連”而成,ASP.NET Web API之所以具有較高的可擴(kuò)展性,主要源于采用的管道式設(shè)計(jì)。雖然ASP.NET Web API框架旨在實(shí)現(xiàn)針對(duì)請(qǐng)求的處理和響應(yīng)的回復(fù),但是采用的處理策略因具體的場(chǎng)景而不同。
我們不可能也沒(méi)有必要?jiǎng)?chuàng)建一個(gè)“萬(wàn)能”的處理器來(lái)滿足所有的請(qǐng)求處理需求,倒不如讓某個(gè)處理器只負(fù)責(zé)某個(gè)單一的消息處理功能。在具體的應(yīng)用場(chǎng)景中,我們可以根據(jù)具體的消息處理需求來(lái)選擇所需的處理器并組成一個(gè)完整的消息處理管道。在這里這個(gè)用于完成某個(gè)單一消息處理功能的處理器就是HttpMessageHandler。
這里的“消息處理”具有兩個(gè)層面的含義,既包括針對(duì)請(qǐng)求消息的處理,還包括針對(duì)響應(yīng)消息的處理。HttpMessageHandler直接或者間接繼承自具有如下定義的抽象類型HttpMessageHandler,該類型定義在命名空間“System.Net.Http”下。ASP.NET Web API通過(guò)類型HttpRequestMessage和HttpResponseMessage來(lái)表示管道處理的請(qǐng)求消息和響應(yīng)消息,所以對(duì)HttpMessageHandler的定義就很好理解了。
1: public abstract class HttpMessageHandler : IDisposable
2: {
3: public void Dispose();
4: protected virtual void Dispose(bool disposing);
5: protected abstract Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
6: }
如上面的代碼片斷所示,抽象類HttpMessageHandler定義了一個(gè)受保護(hù)的抽象方法SendAsync,這是一個(gè)采用針對(duì)Task的“并行編程模式”的異步方法,在后續(xù)的章節(jié)中我們會(huì)看到ASP.NET Web API的應(yīng)用程序接口基本上都采用這樣的定義方式。對(duì)于這個(gè)SendAsync方法來(lái)說(shuō),request參數(shù)表示傳遞給當(dāng)前HttpMessageHandler進(jìn)行處理的請(qǐng)求,這是一個(gè)HttpRequestMessage對(duì)象。另一個(gè)參數(shù)cancellationToken是一個(gè)用于發(fā)送取消操作信號(hào)的CancellationToken對(duì)象,如果讀者對(duì).NET中的并行編程具有基本了解的話,相信對(duì)這個(gè)類型不會(huì)感到陌生。
針對(duì)請(qǐng)求消息和響應(yīng)消息的處理均體現(xiàn)在這個(gè)SendAsync方法上。具體來(lái)說(shuō),針對(duì)請(qǐng)求消息的處理直接實(shí)現(xiàn)在SendAsync方法中,而針對(duì)響應(yīng)消息的處理則通過(guò)其返回的Task對(duì)象來(lái)完成。由HttpMessageHandler組成的消息處理管道以及請(qǐng)求消息和響應(yīng)消息在管道中的“流動(dòng)”基本上可以通過(guò)右圖來(lái)體現(xiàn)。
抽象類HttpMessageHandler實(shí)現(xiàn)了IDisposable接口,它按照“標(biāo)準(zhǔn)”的方式實(shí)現(xiàn)Dispose方法。如下面的代碼片斷所示,當(dāng)我們調(diào)用Dispose方法的時(shí)候,HttpMessageHandler并沒(méi)有執(zhí)行任何資源回收操作。當(dāng)我們通過(guò)繼承這個(gè)抽象類自定義HttpMessagHandler的時(shí)候,可以將資源回收操作實(shí)現(xiàn)在重寫(xiě)的Dispose方法中。
1: public abstract class HttpMessageHandler : IDisposable
2: {
3: //其他操作
4: public void Dispose()
5: {
6: this.Dispose(true);
7: GC.SuppressFinalize(this);
8: }
9:
10: protected virtual void Dispose(bool disposing)
11: {}
12: }
二、DelegatingHandler
我們說(shuō)ASP.NET Web API消息處理管道是通過(guò)一組有序的HttpMessagHandler“首尾相連”而成,具體實(shí)現(xiàn)“管道串聯(lián)”是通過(guò)DelegatingHandler這個(gè)類型來(lái)完成的。顧名思義,DelegatingHandler具有委托功能,當(dāng)它自己負(fù)責(zé)的消息處理任務(wù)完成之后可以委托另一個(gè)HttpMessagHandler進(jìn)行后續(xù)的處理。如果這個(gè)被委托的也是一個(gè)DelegatingHandler對(duì)象,不就可以組成一個(gè)委托鏈了嗎?而這個(gè)委托鏈不就是由一個(gè)個(gè)DelegatingHandler組成的消息處理管道嗎?
如下面的代碼片斷所示,DelegatingHandler是一個(gè)繼承自HttpMessageHandler類的抽象類。上面我們所說(shuō)的這個(gè)被委托的HttpMessagHandler由它的屬性InnerHandler表示。DelegatingHandler重寫(xiě)了定義在其類的抽象方法SendAsync來(lái)調(diào)用InnerHandler屬性的同名方法。
1: public abstract class DelegatingHandler : HttpMessageHandler
2: {
3: protected internal override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
4: public HttpMessageHandler InnerHandler get; set; }
5: }
正如上面所說(shuō),如果ASP.NET Web API的消息處理管道均由DelegatingHandler組成(位于管道尾端的HttpMessageHandler除外),我們就可以根據(jù)其InnerHandler獲得對(duì)被委托的HttpMessageHandler對(duì)象的引用,由此便構(gòu)成具有如上圖所示的鏈?zhǔn)浇Y(jié)構(gòu)。組成ASP.NET Web API核心框架的消息處理管道就這么簡(jiǎn)單。
三、HttpServer
一般來(lái)說(shuō),對(duì)于構(gòu)成ASP.NET Web API消息處理管道的所有HttpMessageHandler來(lái)說(shuō),除了處于尾端的那一個(gè)之外,其余的均為DelegatingHandler,那么通過(guò)InnerHandler屬性維持著“下一個(gè)” HttpMessageHandler。作為這個(gè)HttpMessageHandler鏈“龍頭”的是一個(gè)HttpServer對(duì)象,該類型定義在命名空間“System.Web.Http”下。
如下面的代碼片斷所示,HttpServer直接繼承自DelegatingHandler。它具有兩個(gè)重要的只讀屬性(Configuration和Dispatcher),我們可以通過(guò)前者得到用于配置整個(gè)消息處理管道的HttpConfiguration對(duì)象,另外一個(gè)屬性Dispatcher返回的是處于整個(gè)消息處理管道“尾端”的HttpMessageHandler。
1: public class HttpServer : DelegatingHandler
2: {
3: public HttpConfiguration Configuration { get; }
4: public HttpMessageHandler Dispatcher { get; }
5:
6: public HttpServer();
7: public HttpServer(HttpMessageHandler dispatcher);
8: public HttpServer(HttpConfiguration configuration);
9: public HttpServer(HttpConfiguration configuration, HttpMessageHandler dispatcher);
10:
11: protected override void Dispose(bool disposing);
12: protected virtual void Initialize();
13: protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
14: }
HttpServer的Configuration和Dispatcher屬性均可以在相應(yīng)的構(gòu)造函數(shù)中初始化。如果在構(gòu)造HttpServer的時(shí)候沒(méi)有顯式指定這兩個(gè)屬性的值(調(diào)用默認(rèn)的無(wú)參構(gòu)造函數(shù)創(chuàng)建HttpServer),在默認(rèn)情況下會(huì)創(chuàng)建一個(gè)HttpConfiguration作為Configuration的屬性值,而作為Dispatcher屬性值的則是一個(gè)HttpRoutingDispatcher對(duì)象,該類型定義在命名空間“System.Web.Http.Dispatcher”下。除此之外。由于HttpConfiguration類型實(shí)現(xiàn)了IDisposable接口,所以HttpServer重寫(xiě)了虛方法Dispose并在該方法中完成了對(duì)HttpConfiguration對(duì)象的釋放。
當(dāng)HttpServer對(duì)象被成功創(chuàng)建之后,對(duì)應(yīng)的消息處理管道的“一頭一尾”已經(jīng)確定下來(lái)。一頭指的就是HttpServer對(duì)象本身,一尾指的自然就是通過(guò)Dispatcher屬性引用的HttpMessageHandler對(duì)象了。ASP.NET Web API框架最大的擴(kuò)展性就在于我們可以根據(jù)具體的消息處理需求來(lái)“定制”這個(gè)消息處理管道,它允許我們將自定義的HttpMessageHandler按照如左圖所示的方式“安裝”到這一頭一尾之間,但是這些處于“中間位置”的HttpMessageHandler是如何注冊(cè)呢?
既然整個(gè)管道都是由HttpConfiguration進(jìn)行配置,那么自定義HttpMessageHandler的注冊(cè)自然也可以利用它來(lái)完成。如下面的代碼片斷所示,HttpConfiguration具有一個(gè)只讀的集合類型的MessageHandlers,需要注冊(cè)的HttpMessageHandler需要添加到此集合之中。由于這是一個(gè)元素類型為DelegatingHandler的集合,所以我們自定義的HttpMessageHandler必須繼承自DelegatingHandler。
1: public class HttpConfiguration : IDisposable
2: {
3: //其他成員
4: public Collection MessageHandlers { get; }
5: }
通過(guò)上面的給出的HttpServer類型定義我們可以看到它具有一個(gè)受保護(hù)的Initialize方法,該方法最終完成了對(duì)整個(gè)消息處理管道的構(gòu)建。在重寫(xiě)的SendAsync方法中,如果自身尚未被初始化,該Initialize方法會(huì)自動(dòng)被調(diào)用以確保整個(gè)消息處理管道已經(jīng)被成功構(gòu)建。
四、HttpRoutingDispatcher
在默認(rèn)情況下,作為消息處理管道“龍頭”的HttpServer的Dispatcher屬性返回一個(gè)HttpRoutingDispatcher對(duì)象,它可以視為這個(gè)消息處理管道的最后一個(gè)HttpMessageHandler。Web API調(diào)用請(qǐng)求一般都是針對(duì)定義在某個(gè)HttpController中的某個(gè)Action方法,所以消息處理管道最終需要激活相應(yīng)的HttpController并執(zhí)行對(duì)應(yīng)的Action方法,HttpRoutingDispatcher完成了目標(biāo)HttpController的激活和Action方法的執(zhí)行。
如下面的代碼片斷所示,HttpRoutingDispatcher不再是DelegatingHandler的繼承者,它的直接基類是抽象類HttpMessageHandler。我們?cè)跇?gòu)建一個(gè)HttpRoutingDispatcher對(duì)象的時(shí)候需要指定一個(gè)HttpConfiguration對(duì)象,而通過(guò)參數(shù)defaultHandler指定的HttpMessageHandler對(duì)于創(chuàng)建的HttpRoutingDispatcher對(duì)象來(lái)說(shuō)具有重要的意義,因?yàn)镠ttpController的激活、Action方法的選擇與執(zhí)行等后續(xù)操作實(shí)際上是由它來(lái)完成的。
1: public class HttpRoutingDispatcher : HttpMessageHandler
2: {
3: public HttpRoutingDispatcher(HttpConfiguration configuration);
4: public HttpRoutingDispatcher(HttpConfiguration configuration, HttpMessageHandler defaultHandler);
5:
6: protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
7: }
雖然ASP.NET Web API消息處理管道不具有一個(gè)類似于HttpContext的對(duì)象來(lái)保存基于當(dāng)前請(qǐng)求的上下文信息,但是表示請(qǐng)求消息的HttpRequestMessage對(duì)象具有一個(gè)通過(guò)Properties屬性表示的屬性字典,我們可以利用它來(lái)作為上下文數(shù)據(jù)的存放容器。
通過(guò)上面對(duì)HttpServer的介紹我們知道它會(huì)將當(dāng)前SynchronizationContext和HttpConfiguration添加到表示當(dāng)前請(qǐng)求的HttpRequestMessage對(duì)象的屬性字典中。與之類似,通過(guò)路由系統(tǒng)生成的HttpRouteData也以同樣的方式保存在HttpRequestMessage的屬性字典之中,我們可以直接調(diào)用HttpRequestMessage的如下兩個(gè)擴(kuò)展方法GetRouteData和SetRouteData進(jìn)行HttpRouteData的獲取和設(shè)置。
1: public static class HttpRequestMessageExtensions
2: {
3: //其他成員
4: public static IHttpRouteData GetRouteData(this HttpRequestMessage request);
5: public static void SetRouteData(this HttpRequestMessage request, IHttpRouteData routeData);
6: }
HttpRoutingDispatcher的SendAsync方法被執(zhí)行時(shí),它會(huì)判斷作為參數(shù)的HttpRequestMessage對(duì)象的屬性字典中是否具有這樣一個(gè)HttpRouteData對(duì)象。如果此HttpRouteData對(duì)象存在,它會(huì)直接將請(qǐng)求交付給創(chuàng)建時(shí)指定的HttpMessageHandler進(jìn)行處理。這樣的情況會(huì)發(fā)生在Web Host寄宿模式下。
如果封裝路由數(shù)據(jù)的HttpRouteData對(duì)象尚未添加到表示被處理請(qǐng)求的HttpRequestMessage對(duì)象的屬性字典中,意味著針對(duì)請(qǐng)求的路由尚未發(fā)生,這種情況會(huì)發(fā)生在Self Host寄宿模式下。在這種情況下,HttpRoutingDispatcher會(huì)直接通過(guò)當(dāng)前HttpConfiguration的Routes屬性得到全局路由表,并將HttpRequestMessage對(duì)象作為參數(shù)調(diào)用其GetRouteData方法以實(shí)現(xiàn)針對(duì)當(dāng)前請(qǐng)求的路由解析。
如果執(zhí)行路由表的GetRouteData方法返回一個(gè)具體的HttpRouteData對(duì)象,意味著當(dāng)前請(qǐng)求與注冊(cè)的某個(gè)HttpRoute相匹配,HttpRoutingDispatcher會(huì)將這個(gè)HttpRouteData對(duì)象添加到HttpRequestMessage對(duì)象的屬性字典中。在這之后,ASP.NET Web API會(huì)將請(qǐng)求交付給創(chuàng)建時(shí)指定的HttpMessageHandler進(jìn)行后續(xù)處理。如果執(zhí)行GetRouteData方法返回Null,意味著當(dāng)前請(qǐng)求與注冊(cè)的路由規(guī)則不匹配,客戶端會(huì)得到一個(gè)狀態(tài)為“404, Not Found”的響應(yīng)。
五、HttpControllerDispatcher
我們從類型命名可以看出HttpRoutingDispatcher具有兩個(gè)基本的職能,即“路由(Routing)”和“消息分發(fā)(Dispatching)”。對(duì)于前者,它會(huì)調(diào)用當(dāng)前路由表對(duì)請(qǐng)求消息實(shí)施路由解析進(jìn)而生成用于封裝路由數(shù)據(jù)的HttpRouteData(如果這樣的HttpRouteData不存在于當(dāng)前請(qǐng)求的屬性字典中)。對(duì)于后者,它會(huì)將請(qǐng)求直接分發(fā)給在創(chuàng)建時(shí)指定的HttpMessageHandler來(lái)完成進(jìn)一步處理。
如果在構(gòu)建HttpRoutingDispatcher對(duì)象的時(shí)候沒(méi)有通過(guò)參數(shù)defaultHandler顯式指定這么一個(gè)HttpMessageHandler對(duì)象,默認(rèn)情況下從它手中接管請(qǐng)求的HttpMessageHandler是一個(gè)具有如下定義的HttpControllerDispatcher的對(duì)象,該類型定義在命名空間“System.Web.Http.Dispatcher”下。HttpControllerDispatcher在整個(gè)消息處理管道中顯得尤為重要,因?yàn)槟繕?biāo)HttpController的激活、Action方法的執(zhí)行和響應(yīng)生成均是由HttpControllerDispatcher來(lái)完成的。
1: public class HttpControllerDispatcher : HttpMessageHandler
2: {
3: public HttpControllerDispatcher(HttpConfiguration configuration);
4: protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
5:
6: public HttpConfiguration Configuration { get; }
7: }
在我們引入HttpControllerDispatcher對(duì)象之后,ASP.NET Web API的消息處理管道將具有如右圖所示的結(jié)構(gòu)。從這個(gè)結(jié)構(gòu)來(lái)看,貌似HttpControllerDispatcher才是整個(gè)消息處理管道的最后一個(gè)HttpMessageHandler。這種說(shuō)法沒(méi)有錯(cuò),但我個(gè)人還是傾向于將HttpControllerDispatcher視為“隸屬于” HttpRoutingDispatcher的“內(nèi)部”HttpMessageHandler,所以仍將這個(gè)“包含” HttpControllerDispatcher的HttpRoutingDispatcher視為組成消息處理管道的最后一個(gè)HttpMessageHandler。
除此之外,“N個(gè)DelegagingHandler + 1個(gè)HttpMessageHander”這樣的鏈?zhǔn)浇Y(jié)構(gòu)也剛好與基于DelegagingHandler的委托鏈相匹配。對(duì)于讀者朋友來(lái)說(shuō),具體傾向于哪種說(shuō)法并不重要,重要的是能夠深刻了解整個(gè)消息處理管道的是如何構(gòu)成的。
更多信息請(qǐng)查看IT技術(shù)專欄