Web API中的消息處理程序(Message Handler)


一、消息處理程序的概念

  信息處理程序(Message Handler)接收HTTP請求並返回一個HTTP響應的類。Message Handler繼承 HttpMessageHandler 類。

  通常,一系列消息處理程序協同工作。第一個Message Handler接收HTTP請求,進行一些處理,並將請求提供給下一個Message Handler。在某些時候,響應被創建並返回到Message Handler,此模式稱為委托處理程序(delegating handler)。

二、服務器端消息處理程序

  在服務器端,Web API管道使用一些內置的消息處理程序:

HttpServer                 從主機獲取請求。
HttpRoutingDispatcher      根據請求進行路由。
HttpControllerDispatcher   將請求發送到Web API控制器。

  我們可以向管道添加自定義處理程序。消息處理程序更適合處理Http messages(比起控制器)。因為Message Handler可以:

讀取或修改請求標頭。
為響應添加響應標頭。
在請求到達控制器之前驗證請求。

此圖顯示了插入管道的兩個自定義處理程序:

 

三、自定義Message Handler

第一步:創建Message Handler

  要編寫自定義消息處理程序,請從 System.Net.Http.DelegatingHandler 派生並覆蓋 SendAsync 方法。此方法具有以下簽名:

Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);

  該方法將HttpRequestMessage作為輸入,並異步返回HttpResponseMessage。典型的實現執行以下操作:

1.處理請求消息。
2.調用base.SendAsync將請求發送到內部處理程序。
3.內部處理程序返回響應消息。(此步驟是異步的。)
4.處理響應並將其返回給調用者。

一個簡單的栗子:

public class MessageHandler1 : DelegatingHandler
{
  protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
    Debug.WriteLine("Process request");
    // Call the inner handler.
    var response = await base.SendAsync(request, cancellationToken);
    Debug.WriteLine("Process response");
    return response;
  }
}

調用 base.SendAsync 是異步的。如果處理程序在此調用后執行任何操作,請使用await關鍵字。

委托處理程序也可以跳過inner handler

一個跳過inner handler的栗子:

public class MessageHandler2 : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
      // Create the response.
      var response = new  HttpResponseMessage(HttpStatusCode.OK){
      Content = new StringContent("Hello!")};

      // Note: TaskCompletionSource創建一個不包含委托的task
      var tsc = new TaskCompletionSource<HttpResponseMessage>();
      tsc.SetResult(response); // Also sets the task state to "RanToCompletion"
    return tsc.Task;
  }
}

如果delegate handler在創建響應時不調用base.SendAsync,則請求將跳過管道的其余部分。這對於驗證請求的處理程序(創建錯誤響應)非常有用。

第二步:將處理程序添加到管道

  要在服務器端添加消息處理程序,請將處理程序添加到 HttpConfiguration.MessageHandlers 集合中。

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    config.MessageHandlers.Add(new MessageHandler1());
    config.MessageHandlers.Add(new MessageHandler2());
    // Other code not shown...
  }
}

  消息處理程序的調用順序與它們在MessageHandlers集合中的顯示順序相同。因為它們是嵌套的,所以響應消息反向傳播。

四、自定義消息處理程序實例

4.1  添加自定義響應標頭

這是一個Message Handler,它為每個響應消息添加一個自定義標頭:

 

public class CustomHeaderHandler : DelegatingHandler
{
    async protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
        response.Headers.Add("X-Custom-Header", "This is my custom header.");
        return response;
    }
}

首先,處理程序調用base.SendAsync將請求傳遞給inner handler。inner handler返回響應消息,但它使用Task <T>對象異步執行,在base.SendAsync異步完成之前,響應消息不可用。

4.2  檢查API密鑰

某些Web服務要求客戶在其請求中包含API密鑰。以下示例顯示了消息處理程序如何檢查有效API密鑰的請求:

public class ApiKeyHandler : DelegatingHandler
{
  public string Key { get; set; }
  public ApiKeyHandler(string key)
  {
    this.Key = key;
  }
  //重寫sendAsyc
  protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
    if (!ValidateKey(request))
    {
      //沒有通過則創建response,code為403
      var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
      var tsc = new TaskCompletionSource<HttpResponseMessage>();
      tsc.SetResult(response); 
      return tsc.Task;
    }
    return base.SendAsync(request, cancellationToken);
  }
  //驗證密鑰
  private bool ValidateKey(HttpRequestMessage message)
  {
    var query = message.RequestUri.ParseQueryString();
    string key = query["key"];
    return (key == Key);
  }
}

  Message Handler在URI查詢字符串中查找API密鑰。如果查詢字符串包含Key,則Message handler將請求傳遞給innner handler,沒有key的話返回403。

4.3  Per-Route Message Handler

  HttpConfiguration.MessageHandlers集合中的處理程序全局應用,有時候我們的Message Handler只針對特定的路徑,如url中含有“admin”的,需要登陸后才能訪問:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
     //默認路由
        config.Routes.MapHttpRoute(
            name: "Route1",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
         );
     //路由中指定Message Handler
        config.Routes.MapHttpRoute(
            name: "Route2",
            routeTemplate: "api2/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            handler: new MessageHandler2() // per-route message handler
     );
    //全局的Message Handler 
        config.MessageHandlers.Add(new MessageHandler1()); //     global message handler
    }
}  

在此示例中,如果請求URI與“Route2”匹配,則將分派請求MessageHandler2,如下圖所示:

這時MessageHandler2替換默認的HttpControllerDispatcher。這個栗子中MessageHandler2創建響應,匹配“Route2”的請求永遠不會轉到控制器。這使我們可以使用自己的自定義響應替換整個Web API控制器機制。

我們也可以把路由的MessageHandler委托給 HttpControllerDispatcher ,通過HttpControllerDispatcher調度到控制器。

以下代碼顯示了如何配置此路由:

// delegating handlers.
DelegatingHandler[] handlers = new DelegatingHandler[] {
    new MessageHandler3()
};

// Create a message handler chain with an end-point.
var routeHandlers = HttpClientFactory.CreatePipeline(
    new HttpControllerDispatcher(config), handlers);

config.Routes.MapHttpRoute(
    name: "Route2",
    routeTemplate: "api2/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional },
    constraints: null,
    handler: routeHandlers
);

 

這里總結了Web API中的Message Handler中的概念和基本用法,了解更多用法可以查看官網


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM