介紹自定 HTTP Message Handler 與 HttpClient Message Handler 的方法並說明其運作流程。
前言
上篇 WinForm 使用 HttpClient 呼叫 Web API 針對 HTTP Message Handler 有稍微的說明,這篇文章將針對自定 HTTP Message Handler 說明作法,引述上篇文章提到的內容,在通常情況下,服務收到請求後可能會經過數個 Message Handler 逐步處理後再返回訊息至客戶端,這種處理模式稱為 Delegating Handler,如下圖。
因 Web API 與 HttpClient 都能夠自定訊息處理常式,所以接下來的內容分為 Web API 與 HttpClient 兩部分說明。
Web API 自定 HTTP Message Handler
首先針對 Web API 介紹,以服務端接收到請求後交由訊息處理常式處理的過程來說,會有以下的步驟:
- HttpServer 從主機取得請求。
- HttpRoutingDispatcher 分派請求路由。
- HttpControllerDispatcher 將請求發送至 Web API Controller。
在這個過程裡我們可以將自訂的 Message Handler 插入到這個流程中,而自定的 Message Handler 要處理什麼事情就可以自己來決定,例如增加回應標頭,下圖為一個將自定 Message Handler 插入訊息處理常式通道的示圖。
要自定一個 Message Handler 需要繼承 DelegatingHandler,DelegatingHandler 含於 System.Net.Http 命名空間下,除了繼承 DelegatingHandler 之外還需要覆寫 SendAsync 方法,如下
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken);
}
在 SendAsync 方法中的處理流程如下
- 接收處理請求。
- 呼叫 base.SendAsync() 方法,將請求發送到內部處理程序。
- 內部處理程序將返回響應訊息,注意此時將為非同步處理。
- 處理響應訊息後返回客戶端。
讓我們把以上步驟拆解後會比較清楚,這邊要注意的是因為 base.SendAsync() 是非同步處理方法,所以必須要將 SendAsync() 方法加上 async 與 await 關鍵字。
// 方法須加入 async 關鍵字
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// 1.接受請求
// 2.呼叫 base.SendAsync() 方法,將請求發送至內部處理常式
// 3.因 base.SendAsync() 方法為非同步作業方法,故需在呼叫前加入 await 關鍵字等待作業完成後繼續
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
// 4.處理響應的返回訊息後回傳至客戶端
// do something...
return response;
}
實際上我們可以附加一個自定標頭於回應訊息中,如下:
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("X-Custom-Header", "自定回應標頭標籤");
return response;
}
另外先前有提到能夠定義多個 Message Handler,接下來就來看看定義多個 Message Handler 時運作的流程,如下。
public class Custom1Handler : DelegatingHandler
{
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("X-Custom1-Header", "This is a customer 2 handler.");
return response;
}
}
public class Custom2Handler : DelegatingHandler
{
protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("X-Custom2-Header", "This is a customer 2 handler.");
return response;
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// 省略...
// 註冊 CustomerHandlers
config.MessageHandlers.Add(new Custom1Handler());
config.MessageHandlers.Add(new Custom2Handler());
}
}
使用 Debug 模式透過 WinForm 呼叫服務後查看回應訊息內容的可以發現,回應標頭中多了 X-Custom1 與 X-Custom2。
從以上訊息中可以發現 Message Handler 的確會循序執行所定義的 Custom Message Handler,就如同本節一開始說介紹的那張圖一樣,循序流程如下
- 接收到客戶端請求
- 執行 Custom1Handler 的 base.SendAsync() 方法傳遞訊息
- 執行 Custom2Handler 的 base.SendAsync() 方法傳遞訊息
- 請求傳入內部訊息處理常式至 Controller 處理
- 內部訊息處理常式處理完成後返回響應
- Custom2Handler 收到返回訊息後加入自定標頭2
- Custom1Handler 收到 Custom2Handler 返回訊息後加入自定標頭1
- 返回訊息至客戶端
HttpClient 自定 HttpClient Message Handler
看完了以上 Web API 的自定 Message Handler 的介紹,想必對於 HttpClient 自定 Message Handler 應該是也有些概念了吧,下方是 HttpClient 的自定消息處理常式的通道示圖。
在 HttpClient 自定 Message Handler 基本上是跟 Web API 中差不多,一樣必須透過繼承 DelegatingHandler 或 HttpClientHandler 後覆寫 SendAsync 方法,而其中主要的差異在於 Message Handler 中如需要在發送前修改標頭,則需要在發送請求前處理,如下
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Add("X-Custom-Client-Header", "This is a Client customer handler.");
return base.SendAsync(request, cancellationToken);
}
依照以上作法發送請求後,Web API 收到請求時將帶入自定標頭,我們從 Debug 模式下查看要求的標頭清單,如下
接下來說明該如何在 HttpClient 中指定使用哪一個自定 Message Handler,在 Web API 中我們可以在 WebApiConfig.cs 的 Register 方法中註冊 Message Handler,但是在 HttpClient 中,我們需要使用另外的方式處理。
首先必須要先加入 System.Net.Http.Formatting 組件參考。
在 System.Net.Http.Formatting 命名空間中,提供了 HttpClientFactory 類別可以使用,HttpClientFactory 用於建立新的 HttpClient 執行個體的工廠,我們需要使用 HttpClientFactory 類別的 Create(DelegatingHandler[]) 方法來加入自定的 Message Handler,建立的方式就是透過工廠的 Create 方法並傳入實體化的 Handler 物件,如下
private async void GetAllProducts()
{
HttpClient client = HttpClientFactory.Create(new CustomHandler());
HttpResponseMessage response = await client.GetAsync("http://localhost:49988/api/products");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
ShowResult(JsonConvert.DeserializeObject<List<Product>>(responseBody));
}
透過 HttpClientFactory 的 Create 方法所實體化的 HttpClient 物件,能夠將 HTTP 回應訊息委派給指定的 Message Handler 處理,在此要注意的是如傳入多個 Message Handler,當發送請求時執行順序是由第一個傳入的 Message Handler 開始,但是當接收返回訊息時,最先收到返回訊息的 Message Handler 將是傳入順序的最後一個 Message Handler,以上就是自定 Message Handlers 的說明。
範例程式碼
參考資料
以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教
如有侵權內容也請您與我反應~謝謝您 :)