前言
我們知道Web API本身是無法提供請求-響應的機制,它是通過Web Host以及Self Host的寄宿的宿主方式來提供一個請求-響應的運行環境。二者都是將請求和響應抽象成HttpResponseMessage和HttpRequesMessage對象,並將請求HttpRequestMessage傳入到HttpMessageHandler進行處理最終將響應通過HttpResponseMessage逆向通過HttpMessageHandler返回到客戶端,但是在其過程中,此二者在管道中的機制是不一樣的,由於在最新Web API中是以Web Host寄宿實現,所以僅本節僅討論Web Host寄宿的實現。
Web Host
Web API采用Web Host寄宿模式,其路由系統最終還是通過ASP.NET的路由系統來實現,也就是說其本質是將ASP.NET應用程序作為Web API的宿主,利用ASP.NET自身的路由系統並結合IIS來實現去持續監聽以及請求和響應的問題。
既然Web Host寄宿是利用ASP.NET的路由系統實現,那么我們首先就得了解下ASP.NET的路由系統,ASP.NET的路由是利用UrlRoutingModel中的HttpModel來完成,並通過HttpApplication中的PostResloveRequesCache事件對其請求進行攔截,並借助注冊的路由將請求的URL進行解析獲得路由數據對象RouteData,UrlRoutingModel最終從匹配對象Route對象中獲取對應的HttpHandler並映射給當前上下文HttpContext,緊接着上下文HttpContext獲取請求並進行響應。
上述聽起來似乎有點拗口並且不太能讓人理解,我們首先來了解下ASP.NET的請求管道,當一個請求過來時首先會經過擴展名來進行相應的處理,如果是靜態文件則直接返回到客戶端,若是動態文件如擴展名為.aspx,則將交給aspnet.isapi.dll來進行處理,然后就是創建一個ASP.NET運行環境HttpRuntime,在創建運行環境的同時首先創建一個HttpWorkerRquest對象,這個對象則保存着請求的報文信息,接着借助HttpWorkerRequest對象創建上下文 HttpContext (包含着HttpResponse和HttpRequest以及HttpSessionState等)接下來就是通過ApplicationFactory工廠創建一個上述所說的 HttpApplication 對象,此對象為管道事件的核心對象,然后調用ProcessRequest方法將上下文HttpContext傳入其中,最終通過HttpApplication來執行19個管道事件,在前八個事件利用 HttpModel 進行相關的驗證、授權等,在第八個事件則創建頁面類對象同時實現IHttpHandler接口創建一個我們上述所說的 HttpHandler ,后面大概就是創建頁面控件樹以及執行頁面對象的頁面生命周期等。
通過上述敘述知,Web API利用Web Host作為寄宿則利用UrlRoutingModel來動態映射給HttpContext中的HttpHandler是實現管道集成的核心,它會將ASP.NET中的HttpRequest對象表示的請求轉換成HttpRequestMessage對象並傳入到消息管道中,並將輸出的HttpResponseMessage寫入到HttpResponse中並最終進行響應輸出。現在最主要的問題是這個HttpHandler在Web Host模式下是怎樣實現的?請繼續往下看。
HttpControllerRouteHandler
當通過Web API的配置文件通過模板以及約束等注冊一個HttpRoute路由對象到路由集合中時也就是將其注冊到路由表中(因為路由表中的屬性Routes存着注冊的路由的路由集合),首先會創建一個HostedHttpRoute對象,我們看看該對象的定義:

其中最重要的是OriginalRoute屬性,照定義來看就僅僅是創建了這個對象而已,確確實實是這樣,因為該對象僅僅是起一個過渡作用,因為真正創建的對象是HttpWebRoute的Route對象,通過查看其構造函數可知,如下:
public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler) { RouteValueDictionary dictionary; RouteValueDictionary dictionary2; RouteValueDictionary dictionary3; base..ctor(); dictionary = (defaults != null) ? new RouteValueDictionary(defaults) : null; dictionary2 = (constraints != null) ? new RouteValueDictionary(constraints) : null; dictionary3 = (dataTokens != null) ? new RouteValueDictionary(dataTokens) : null; this.OriginalRoute = new HttpWebRoute(uriTemplate, dictionary, dictionary2, dictionary3, HttpControllerRouteHandler.Instance, this); this.Handler = handler; return; }
如上紅色標記,此時將屬性OriginalRoute作為HttpWebRoute對象的引用存在HostedHttpRoute中並返回一個Route對象。
那么上述所說的RouteData是什么時候生成呢?
我們再來看看這個Route對象的定義:

當請求過來時請求URL會與注冊的路由(Route)進行匹配,若匹配成功,由上知,此時同時會通過GetRouteData方法生成一個 RouteData 對象,我們同時看看該RouteData的定義:
public class RouteData { // Fields private RouteValueDictionary _dataTokens; private IRouteHandler _routeHandler; private RouteValueDictionary _values; // Methods public RouteData(); public RouteData(RouteBase route, IRouteHandler routeHandler); public string GetRequiredString(string valueName); // Properties public RouteValueDictionary DataTokens { get; } public RouteBase Route { get; set; } public IRouteHandler RouteHandler { get; set; } public RouteValueDictionary Values { get; } }
此對象中有一個匹配路由Route的引用屬性,也就是當前匹配成功的Route對象並且還有返回值為IRouteHandler的屬性RouteHandler。
所以在注冊路由的過程是:
HttpRoute-------------->HostedHttpRoute-------------->HttpWebRoute------------->Route
在HttpWebRoute繼承自Route類中有一個屬性RouteHandler如下:
public IRouteHandler RouteHandler { [CompilerGenerated] get; [CompilerGenerated] set; }
我們通過如下HttpControllerRouteHandler的定義知,上述RouteHandler就是一個HttpControllerRouteHandler對象,因為HttpControllerRouteHandler實現了接口IRouteHandler,如下:
public class HttpControllerRouteHandler : IRouteHandler { // Fields private static readonly Lazy<HttpControllerRouteHandler> _instance; [CompilerGenerated] private static Func<HttpControllerRouteHandler> CS$<>9__CachedAnonymousMethodDelegate1; // Methods static HttpControllerRouteHandler(); protected HttpControllerRouteHandler(); [CompilerGenerated] private static HttpControllerRouteHandler <.cctor>b__0(); protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext); IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext); // Properties public static HttpControllerRouteHandler Instance { get; } }
注意上述標記,通過GetHttpHandler方法知,在ASP.NET路由中的HttpHandler就是由HttpControllerRouteHandler對象提供的,同時我們看下該方法的定義:
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { return new HttpControllerHandler(requestContext.RouteData); }
看見這個方法的返回值沒,又涉及到一個對象,其實通過HttpControllerRouteHandler中的方法獲得的HttpHandler其實是一個HttpControllerHandler對象,而整個消息處理管道則是有它來創建的。請繼續往下看!
HttpControllerHandler
上面說過此對象是Web Host寄宿模式下的整個管道的執行者,下面我們就來看看這個類的定義(操蛋,太長了還是就看下這個類的繼承,再敘述其他的再給代碼):
public class HttpControllerHandler : HttpTaskAsyncHandler { }
再看下其父類的定義:
public abstract class HttpTaskAsyncHandler : IHttpAsyncHandler, IHttpHandler { // Methods protected HttpTaskAsyncHandler(); [EditorBrowsable(EditorBrowsableState.Never)] public virtual void ProcessRequest(HttpContext context); public abstract Task ProcessRequestAsync(HttpContext context); IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData); void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result); // Properties public virtual bool IsReusable { get; } }
簡單敘述下,HttpRouteHandler是HttpTaskAsyncHadler的子類,並且其父類為實現了IHttpAsync和IHttpHandler接口的抽象類。最主要的是HttpTaskAsyncHandler調用了實現IHttpAsyncHandler接口的BeginProcessRequest方法,並返回一個IAsyncResult,而EndProcessRequest方法則用來執行了返回值為IAsyncResult的BeginProcessRequest方法。
我們再來看看HttpRouteHandler的一個構造函數:
public HttpControllerHandler(RouteData routeData, HttpMessageHandler handler);
該構造函數的第一個參數就是根據路由解析得到的路由數據,第二個參數就是管道中的處理程序,實質上就是第一個HttpMessageHandler即管道頭(HttpServer)。
下面我們總結下在ASP.NET路由系統中HttpControllerHandler被創建的整個過程。請看!
總結
當進入ASP.NET請求管道中時,在HttpModel中通過事件對其請求進行攔截后,然后利用UrlRoutingModel中注冊的路由對象對當前請求的URL進行匹配,若匹配通過由對應匹配的路由解析並生成一個RouteData對象,當然這個Route對象就是HttpWebRoute對象,接着利用RouteData對象對應的Route來獲得RouteHandler,這個RouteHandler就是HttpControllerRouteHandler,接着利用UrlRoutingModel中得到的RouteData和當前上下文HttpContext生成一個請求上下文對象,再以該請求上下文對象為對象調用HttpControllerRouteHandler上的GetHttpHandler方法獲得HttpHandler(返回的是HttpControllerHandler),並將HttpHandler映射到當前上下文HttpContext中,然后調用HttpControllerHandler上繼承自IHttpAsyncHandler上的BeginProcessRequest方法開始進入Web API管道。
下面我們實現IHttpModel接口並對照上述敘述進行編碼,如下:
public class WebHost : IHttpModule { public void Dispose() { throw new NotImplementedException(); } public void Init(HttpApplication context) { context.PostResolveRequestCache += context_PostResolveRequestCache; } void context_PostResolveRequestCache(object sender, EventArgs e) { var app = sender as HttpApplication; var contextWrapper = new HttpContextWrapper(app.Context); var routeData = RouteTable.Routes.GetRouteData(contextWrapper); var requestContext = new RequestContext(contextWrapper, routeData); var httpHandler = routeData.RouteHandler.GetHttpHandler(requestContext); var httpAsyncHandler = httpHandler as IHttpAsyncHandler; httpAsyncHandler.BeginProcessRequest(app.Context, null, null); } }
總結
下面就Web API中Web Host寄宿模式下的消息處理管道給出整體示意圖:來自【Web Host管道】

