最近在看老A的《ASP.NET Web API 框架揭秘》,這本書對於本人現階段來說還是比較合適的(對於調用已經較為熟悉,用其開發過項目,但未深入理解過很多內容為何可以這樣“調用”)。看到第四章了,有些內容看着雖能理解,但未遇到過具體的問題,看起來也就沒有豁然開朗之感。同時,有些內容是一眼就覺得“這是干貨”,可能是之前遇到過某些問題,當時用一些搓辦法解決,但現在看到書中的示例就如獲至寶。所以,就挑些單獨能拎出來且馬上就能在項目中應用的內容出來與大家分享交流下吧。其中會穿插一些框架中的知識點。不合適之處,請指教。萬分感謝。
起因:理想的RESTful Web API采用面向資源的架構,並使用請求的HTTP方法表示針對目標資源的操作類型,但是理想和現實是有距離的。雖然HTTP協議提供了一系列原生的HTTP方法,但是在具體的網絡環境中,很多是不支持的。比如:
1、有的瀏覽器只能發送GET和POS請求,客戶端發送的PUT請求也不一定能夠被服務器理解。
2、除了客戶端和服務器對請求采用的HTTP方法的制約外,像代理(Proxy)、網關(Gateway)等這些中間部件都具有針對HTTP方法的限制
如何解決呢,我們一般采用“http方法重寫”來解決這個問題。本示例自定義HttpMessageHandler(目的是替換請求的HTTP方法)實現HTTP方法重寫。
具體步驟如下:
一、新建一個空的ASP.NET Web API應用,創建一個DemoController
這個就不多說了,直接貼個代碼。
定義了不同Http方法對應的訪問接口,同時返回對應的Http方法名,方便下面對其進行發送請求后,通過其返回值判斷具體請求時是采用的哪個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 23 }
二、創建一個HttpMessageHandler實現Http方法的覆蓋,注冊到Web API的消息處理管道中
這個有個名詞,管道,什么意思呢?書上是這么定義的,ASP.NET Web API的核心框架是一個消息處理管道,這個管道是一組HttpmessageHandler的有序組合。這是一個雙工管道,請求消息從一端流入並依次經過所有HttpMessageHandler的處理。在另一端,目標HttpController被激活,Action方法被執行,響應消息隨之被生成。
初看,有點糊塗,不知道是我自身的底子不夠還是為何,一堆文字下來,我會暈,但好在有代碼,看完代碼,再看文字,理解起來,舒服得多。
上面這段在我目前的理解來看就是:為何你敲個Uri,ASP.NET Web API就能找到對應的action執行並返回呢,這其實在瀏覽器(客戶端)發起請求到action執行這段過程中經過了一系列的操作,這一系列操作被分門別類的分離開來,每一個都可視為單獨存在,稱之為一個HttpMessageHandler,多個HttpMessageHandler組成了所謂管道。你可以先將其理解為一堆過濾器。
再來說我們要創建的這個HttpMessageHandler,作用就是實現Http方法的覆蓋,即重寫。我們創建完畢后,將這玩意兒加入到Web API的消息處理管道中,這樣我們的請求過來后就必須經過我們自定義的HttpMessageHandler的“審查”。
實現:
1、新建我們的HttpMessageHandler,重寫方法SendAsync,關鍵其實就是把請求頭中的key-value的"X-HTTP-Method-Override"取出來,覆蓋掉請求自身的Http方法,為何叫這名字呢?約定俗成,或者叫--規矩?
(如果跟我在一個頻道上的朋友可能注意到,現在有個問題就是如何設置"X-HTTP-Method-Override"的值,這個待會兒再講0 0)
public class HttpMethodOverrideHandler:DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { IEnumerable<string> methodOverrideHeader; if (request.Headers.TryGetValues("X-HTTP-Method-Override",out methodOverrideHeader)) { //這樣就將請求自身的HTTP方法替換成"X-HTTP-Method-Override"內定義的方法了 request.Method = new HttpMethod(methodOverrideHeader.First()); } return base.SendAsync(request, cancellationToken); } }
這里的DelegatingHandler即繼承自HttpMessageHandler
2、HttpMessageHandler建立好后,那就是要添加到管道中
很簡單,在我們的Global.asax.cs中的Application_Start()中添加如下一行代碼
GlobalConfiguration.Configuration.MessageHandlers.Add(new HttpMethodOverrideHandler());
至此,服務端的工作完畢。我們已經在一個空的ASP.NET Web API項目中添加一個可供訪問的demoController以及創建了一個用於Http方法重寫(通過獲取請求頭的X-HTTP-Method-Override來覆蓋Http方法)的HttpMessageHandler且加入到Web API的消息處理管道中了。接下來就是客戶端模擬調用了。
三、創建一個客戶端,進行訪問測試
對於客戶端,本身並無要求,可以是web頁面瀏覽器來調用,也可以寫個winform,wpf之類。這里就用控制台來進行示例,書中也是控制台,待會兒我還會用web頁面上JS來調用,畢竟這才是我們工作的常態。
1、新建一個空的控制台應用層程序
直接貼代碼 Program.cs
創建了四個請求,1跟2的請求頭未設置X-HTTP-Method-Override,1自身的Http方法設置為Get,其他三個均為Post。
1 class Program 2 { 3 4 static void Main(string[] args) 5 { 6 HttpClient httpClient1 = new HttpClient(); 7 HttpClient httpClient2 = new HttpClient(); 8 HttpClient httpClient3 = new HttpClient(); 9 HttpClient httpClient4 = new HttpClient(); 10 11 httpClient3.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "PUT"); 12 httpClient4.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "DELETE"); 13 14 Console.WriteLine("{0,-7}{1,-24}{2,-12}{3,-24}","Method", "X-HTTP-Method-Override","Action","第幾次請求"); 15 16 InvokeWebApi(httpClient1, HttpMethod.Get,1); 17 InvokeWebApi(httpClient2, HttpMethod.Post,2); 18 InvokeWebApi(httpClient3, HttpMethod.Post,3); 19 InvokeWebApi(httpClient4, HttpMethod.Post,4); 20 21 Console.Read(); 22 } 23 24 async static void InvokeWebApi(HttpClient httpClient, HttpMethod method,int requestTime) 25 { 26 string requestUri = "http://localhost:52697/api/demo";//上面創建的web api項目的地址 27 HttpRequestMessage request = new HttpRequestMessage(method, requestUri); 28 HttpResponseMessage response = await httpClient.SendAsync(request); 29 IEnumerable<string> methodsOverride; 30 31 httpClient.DefaultRequestHeaders.TryGetValues("X-HTTP-Method-Override", out methodsOverride); 32 string actionName = response.Content.ReadAsStringAsync().Result; 33 string methodOverride = methodsOverride == null ? "N/A" : methodsOverride.First(); 34 Console.WriteLine("{0,-7}{1,-24}{2,-12}{3,-24}", method, methodOverride, actionName.Trim('"'), requestTime); 35 } 36 }
代碼很簡單,按照我們之前講的,輸出不出問題的話,應該的設置了X-HTTP-Method-Override的請求,自身Http請求方法會被請求頭中X-HTTP-Method-Override對應的值給覆蓋掉。因此,得到如下輸出結果:
第3、4次請求的Http方法均被“重寫”了。任務完成。
有的同學要提問了,那么我們用JS發起ajax請求的時候如何操作呢,關鍵其實就是ajax如何操作請求頭。這里同樣給個示例
在上面的Web API項目中添加個HTML頁面(這里用jquery操作ajax)添加代碼如下:
若不出意外,按照我們的設想,應該是彈出“PUT”,而非“POST”,那么事實呢?
bingo~~睡覺!好久沒更新博客園了,不能懶惰了。