文章出處

理想的RESTful Web API采用面向資源的架構,并使用請求的HTTP方法表示針對目標資源的操作類型。但是理想和現實是有距離的,雖然HTTP協議提供了一系列原生的HTTP方法,但是在具體的網絡環境中,很多是不支持的。比如有的瀏覽器只能發送GET和POST請求,客戶端發送的PUT請求也不一定能夠被服務器理解。除了客戶端和服務器對請求采用的HTTP方法的制約外,像代理(Proxy)、網關(Gateway)等這些中間部件都具有針對HTTP方法的限制。[本文已經同步到《How ASP.NET Web API Works?》]

我們一般采用“HTTP方法重寫”的方式來解決這個問題。具體來說,Web API依然針對標準HTTP方法具有的資源操作語義來定義。客戶端發送的請求只能采用網絡允許的HTTP方法(一般來說,GET和POST總是被支持的),但是與資源操作方式相匹配的HTTP方法名稱會通過一個請求報頭發送給服務器。服務器在根據請求實施操作選擇之前,它會提取該請求報頭攜帶的HTTP方法,請求自身的HTTP方法會被它重寫或者覆蓋。按照約定,我們將這個攜帶“覆蓋當前請求HTTP方法”的報頭命名為“X-HTTP-Method-Override”。

ASP.NET Web API采用管道式的設計,這個旨在解決部分HTTP方法在網絡環境中不被支持的HTTP方法重寫機制可以很容易地通過自定義HttpMessageHandler來實現。具體來說,由于消息處理管道根據表示請求的HttpRequestMessage對象的Method屬性確定請求采用的HTTP方法,并且這是一個可讀寫的屬性,如果我們利用注冊的HttpMessageHandler根據“X-HTTP-Method-Override”報頭值來設置當前HttpRequestMessage的Method屬性,那么管道后續部分將會針對這個覆蓋的HTTP方法進行處理。

為此我們定義了如下一個HttpMethodOverrideHandler類型,它繼承自DelegatingHandler。我們在重寫的SendAsync方法中實現了對“X-HTTP-Method-Override”報頭的提取和對HTTP方法的重寫,最后調用基類的同名方法將處理后的請求傳遞給后續的HttpMessageHandler。

   1: public class HttpMethodOverrideHandler: DelegatingHandler
   2: {
   3:     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   4:     {            
   5:         IEnumerable<string> methodOverrideHeader;
   6:         if (request.Headers.TryGetValues("X-HTTP-Method-Override", out methodOverrideHeader))
   7:         {
   8:             request.Method = new HttpMethod(methodOverrideHeader.First());
   9:         }
  10:         return base.SendAsync(request, cancellationToken);
  11:     }
  12: }

我們在一個空ASP.NET Web API應用中定義了如下一個繼承自ApiController的DemoController,并在其中定義了4個用于返回自身方法名稱的Action方法(Get、Post、Put和Delete)。按照ASP.NET Web API默認提供的HTTP方法與Action方法名稱之間的映射機制,這4個Action方法支持HTTP方法與自身的方法名稱一致。

   1: public class DemoController : ApiController
   2: {
   3:     public string Get()
   4:     {
   5:         return "Get";
   6:     }
   7:  
   8:     public string Post()
   9:     {
  10:         return "Post";
  11:     }
  12:  
  13:     public string Put()
  14:     {
  15:         return "Put";
  16:     }
  17:  
  18:     public string Delete()
  19:     {
  20:         return "Delete";
  21:     }
  22: }

在Global.asax文件中,我們采用如下的代碼將一個HttpMethodOverrideHandler對象注冊到ASP.NET Web API的消息處理管道中。我們采用IIS Express作為宿主,并將采用的端口固定為“3721”。

   1: public class WebApiApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         GlobalConfiguration.Configuration.MessageHandlers.Add(new HttpMethodOverrideHandler());
   6:         //其他操作
   7:     }
   8: }

我們創建一個控制臺應用作為調用Web API的客戶端程序。如下面的代碼片斷所示,我們定義了一個輔助方法InvokeWebApi根據提供的HttpClient對象和請求采用的HTTP方法進行Web API的調用。在該方法中,我們根據指定的HTTP方法創建了一個指向目標Web API的HttpRequestMessage對象,并將其作為參數調用HttpClient對象的SendAsync方法對目標Web API發起調用。Web API成功調用后會得到最終被執行的目標Action方法的名稱,我們將它連同當前請求采用的HTTP方法和“X-HTTP-Method-Override”報頭值打印在控制臺上。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         HttpClient httpClient1 = new HttpClient();
   6:         HttpClient httpClient2 = new HttpClient();
   7:         HttpClient httpClient3 = new HttpClient();
   8:         HttpClient httpClient4 = new HttpClient();
   9:  
  10:         httpClient3.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "PUT");
  11:         httpClient4.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "DELETE");
  12:  
  13:         Console.WriteLine("{0,-7}{1,-24}{2,-6}", "Method", "X-HTTP-Method-Override", "Action");
  14:         InvokeWebApi(httpClient1, HttpMethod.Get);
  15:         InvokeWebApi(httpClient2, HttpMethod.Post);
  16:         InvokeWebApi(httpClient3, HttpMethod.Post);
  17:         InvokeWebApi(httpClient4, HttpMethod.Post);
  18:  
  19:         Console.Read();
  20:     }
  21:  
  22:     async static void InvokeWebApi(HttpClient httpClient, HttpMethod method)
  23:     {
  24:         string requestUri = "http://localhost:3721/api/demo";
  25:         HttpRequestMessage request = new HttpRequestMessage(method, requestUri);
  26:         HttpResponseMessage response = await httpClient.SendAsync(request);
  27:         IEnumerable<string> methodsOverride;
  28:         httpClient.DefaultRequestHeaders.TryGetValues("X-HTTP-Method-Override", out methodsOverride);
  29:         string actionName = response.Content.ReadAsStringAsync().Result;
  30:         string methodOverride = methodsOverride == null ? "N/A" : methodsOverride.First();
  31:         Console.WriteLine("{0,-7}{1,-24}{2,-6}", method, methodOverride, actionName.Trim('"'));
  32:     }
  33: }

在Main方法中,我們創建了4個HttpClient對象(httpClient1、httpClient2、httpClient3和httpClient4),并將“X-HTTP-Method-Override”報頭添加到httpClient3和httpClient4的默認報頭集合中,指定的HTTP方法分別是“PUT”和“DELETE”。我們將這4個HttpClient對象作為參數調用輔助方法InvokeWebApi對目標Web API發起4次調用,除了第1次(由于InvokeWebApi是一個異步方法,代碼中的第一次調用并不意味著它首先被執行,更不能確保針對它的Web API調用率先完成)采用GET請求之外,其余請求均采用POST方法。

在啟動Web API宿主程序后運行客戶端控制臺應用,我們會得到如下所示的輸出結果。我們可以清楚地看到在請求不具有“X-HTTP-Method-Override”報頭的情況下,執行的Action方法取決于請求采用的HTTP方法。反之,如果請求通過“X-HTTP-Method-Override”報頭攜帶了相應的HTTP方法,它將用于目標Action方法的選擇。這一切均是HttpMethodOverrideHandler這個自定義HttpMessageHandler的功勞。

   1: Method X-HTTP-Method-Override  Action
   2: POST   PUT                     Put
   3: GET    N/A                     Get
   4: POST   N/A                     Post
   5: POST   DELETE                  Delete

文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()