亲而有间,密而有疏;和而不同,美美与共

学习ASP.NET Web API框架揭秘之“HTTP方法重写”

  最近在看老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~~睡觉!好久没更新博客园了,不能懒惰了。

 

posted @ 2016-10-07 00:04  大兄弟竹子  阅读(2679)  评论(0编辑  收藏  举报