如果调用ASP.NET Web API不能发送PUT/DELETE请求怎么办?

理想的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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术博客

设计模式之四(抽象工厂模式第一回合)

首先关于抽象工厂模式的学习,我们需要慢慢的,由浅入深的进入。不能单刀直入,否则可能达不到预期学明白的目标。

11910
来自专栏分布式系统和大数据处理

C#网络编程(异步传输字符串) - Part.3

这篇文章我们将前进一大步,使用异步的方式来对服务端编程,以使它成为一个真正意义上的服务器:可以为多个客户端的多次请求服务。但是开始之前,我们需要解决上一节中遗留...

14030
来自专栏博客园

.NET面试题解析(07)-多线程编程与线程同步

转自:http://www.cnblogs.com/anding/p/5301754.html

15540
来自专栏极客猴

Django学习之旅(四)

因为自己看了其他方面的书,所以Django的学习计划暂时搁浅。我这周重新恢复计划,Django学习之旅第四篇文章姗姗来迟。本文主要讲述视图的一些高级用法,包括 ...

10120
来自专栏田京昆的专栏

Memcached 与 Redis 实现的对比

memcached 和 redis,作为近些年最常用的缓存服务器,相信大家对它们再熟悉不过了。前两年还在学校时,我曾经读过它们的主要源码,如今写篇笔记从个人角度...

34.2K190
来自专栏大内老A

通过添加HTTP Header实现上下文数据在WCF的自动传递

多年之前,我写了一篇通过WCF扩展实现上下文信息从客户端自动传递到服务端的文章,其实现机制很简单:将上下文信息存放到SOAP Header进行传递。那么对于非S...

502100
来自专栏大内老A

利用EntLib授权机制实现对ASP.NET页面的自动授权

ASP.NET默认采用UrlAuthorizationModule和FileAuthorizationModule分别实现针对请求地址和物理文件的授权,但是在很...

24090
来自专栏大内老A

了解ASP.NET MVC几种ActionResult的本质:HttpStatusCodeResult & RedirectResult/RedirectToRouteResult

在本系列的最后一篇,我们来讨论最后三个ActionResult:HttpStatusCodeResult、RedirectResult和RedirectToRo...

244100
来自专栏菩提树下的杨过

Ado.Net连接池的速度测试

晚上闲来无事,突然想测试一下Ado.Net连接池带来的连接速度提升,写了以下代码: using System; using System.Configura...

22260
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-WebApi与Unity注入

前言: 有时候我们系统需要开放数据给手机App端或其他移动设备,不得不说Asp.net WebApi是目前首选 本节记录Asp.net MVC WebApi怎么...

27050

扫码关注云+社区

领取腾讯云代金券