C#:只支持GET和POST方法的浏览器,如何发送PUT/DELETE请求?RESTful WebAPI如何响应?
理想的RESTful WebAPI采用面向资源的架构,并使用请求的HTTP方法表示针对目标资源的操作类型。但是理想和现实是有距离的,虽然HTTP协议提供了一系列原生的HTTP方法,但是在具体的网络环境中,很多是不支持的。比如有的浏览器只能发送GET和POST请求,客户端发送的PUT请求也不一定能够被服务器理解。除了客户端和服务器对请求采用的HTTP方法的制约外,像代理(Proxy)、网关(Gateway)等这些中间部件都具有针对HTTP方法的限制。
我们一般采用HTTP方法重写
的方式来解决这个问题。具体来说,WebAPI依然针对标准HTTP方法具有的资源操作语义来定义。客户端发送的请求只能采用网络允许的HTTP方法(一般来说,GET和POST总是被支持的),但是与资源操作方式相匹配的HTTP方法名称会通过一个请求报头发送给服务器。服务器在根据请求实施操作选择之前,它会提取该请求报头携带的HTTP方法,请求自身的HTTP方法会被它重写或者覆盖。按照约定,我们将这个携带覆盖当前请求HTTP方法
的报头命名为X-HTTP-Method-Override
。
一、ASP.NET WebAPI
ASP.NET WebAPI采用管道式的设计,这个旨在解决部分HTTP方法在网络环境中不被支持的HTTP方法重写机制可以很容易地通过自定义HttpMessageHandler来实现。具体来说,由于消息处理管道根据表示请求的HttpRequestMessage对象的Method属性确定请求采用的HTTP方法,并且这是一个可读写的属性,如果我们利用注册的HttpMessageHandler根据X-HTTP-Method-Override
报头值来设置当前HttpRequestMessage的Method属性,那么管道后续部分将会针对这个覆盖的HTTP方法进行处理。
为此我们定义了如下一个HttpMethodOverrideHandler类型,它继承自DelegatingHandler。我们在重写的SendAsync方法中实现了对X-HTTP-Method-Override
报头的提取和对HTTP方法的重写,最后调用基类的同名方法将处理后的请求传递给后续的HttpMessageHandler。
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))
{
request.Method = new HttpMethod(methodOverrideHeader.First());
}
return base.SendAsync(request, cancellationToken);
}
}
我们在一个空ASP.NET WebAPI应用中定义了如下一个继承自ApiController的DemoController,并在其中定义了4个用于返回自身方法名称的Action方法(Get、Post、Put和Delete)。按照ASP.NET WebAPI默认提供的HTTP方法与Action方法名称之间的映射机制,这4个Action方法支持HTTP方法与自身的方法名称一致。
public class DemoController : ApiController
{
public string Get()
{
return "Get";
}
public string Post()
{
return "Post";
}
public string Put()
{
return "Put";
}
public string Delete()
{
return "Delete";
}
}
在Global.asax文件中,我们采用如下的代码将一个HttpMethodOverrideHandler对象注册到ASP.NET WebAPI的消息处理管道中。我们采用IIS Express作为宿主,并将采用的端口固定为“3721”。
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new HttpMethodOverrideHandler());
//其他操作
}
}
我们创建一个控制台应用作为调用WebAPI的客户端程序。如下面的代码片断所示,我们定义了一个辅助方法InvokeWebApi根据提供的HttpClient对象和请求采用的HTTP方法进行WebAPI的调用。在该方法中,我们根据指定的HTTP方法创建了一个指向目标WebAPI的HttpRequestMessage对象,并将其作为参数调用HttpClient对象的SendAsync方法对目标WebAPI发起调用。WebAPI成功调用后会得到最终被执行的目标Action方法的名称,我们将它连同当前请求采用的HTTP方法和X-HTTP-Method-Override
报头值打印在控制台上。
class Program
{
static void Main(string[] args)
{
HttpClient httpClient1 = new HttpClient();
HttpClient httpClient2 = new HttpClient();
HttpClient httpClient3 = new HttpClient();
HttpClient httpClient4 = new HttpClient();
httpClient3.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "PUT");
httpClient4.DefaultRequestHeaders.Add("X-HTTP-Method-Override", "DELETE");
Console.WriteLine("{0,-7}{1,-24}{2,-6}", "Method", "X-HTTP-Method-Override", "Action");
InvokeWebApi(httpClient1, HttpMethod.Get);
InvokeWebApi(httpClient2, HttpMethod.Post);
InvokeWebApi(httpClient3, HttpMethod.Post);
InvokeWebApi(httpClient4, HttpMethod.Post);
Console.Read();
}
async static void InvokeWebApi(HttpClient httpClient, HttpMethod method)
{
string requestUri = "http://localhost:3721/api/demo";
HttpRequestMessage request = new HttpRequestMessage(method, requestUri);
HttpResponseMessage response = await httpClient.SendAsync(request);
IEnumerable<string> methodsOverride;
httpClient.DefaultRequestHeaders.TryGetValues("X-HTTP-Method-Override", out methodsOverride);
string actionName = response.Content.ReadAsStringAsync().Result;
string methodOverride = methodsOverride == null ? "N/A" : methodsOverride.First();
Console.WriteLine("{0,-7}{1,-24}{2,-6}", method, methodOverride, actionName.Trim('"'));
}
}
在Main方法中,我们创建了4个HttpClient对象(httpClient1、httpClient2、httpClient3和httpClient4),并将X-HTTP-Method-Override
报头添加到httpClient3和httpClient4的默认报头集合中,指定的HTTP方法分别是“PUT”和“DELETE”。我们将这4个HttpClient对象作为参数调用辅助方法InvokeWebApi对目标WebAPI发起4次调用,除了第1次(由于InvokeWebApi是一个异步方法,代码中的第一次调用并不意味着它首先被执行,更不能确保针对它的WebAPI调用率先完成)采用GET请求之外,其余请求均采用POST方法。
在启动WebAPI宿主程序后运行客户端控制台应用,我们会得到如下所示的输出结果。我们可以清楚地看到在请求不具有X-HTTP-Method-Override
报头的情况下,执行的Action方法取决于请求采用的HTTP方法。反之,如果请求通过X-HTTP-Method-Override
报头携带了相应的HTTP方法,它将用于目标Action方法的选择。这一切均是HttpMethodOverrideHandler这个自定义HttpMessageHandler的功劳。
Method X-HTTP-Method-Override Action
POST PUT Put
GET N/A Get
POST N/A Post
POST DELETE Delete
二、ASP.NET Core WebAPI
通过注册中间件来解决这个问题
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHttpMethodOverride();
}
public static class HttpMethodOverrideExtensions
{
/// <summary>
/// Allows incoming POST request to override method type with type specified in header.
/// </summary>
/// <param name="builder">The <see cref="T:Microsoft.AspNetCore.Builder.IApplicationBuilder" /> instance this method extends.</param>
public static IApplicationBuilder UseHttpMethodOverride(this IApplicationBuilder builder)
{
if (builder == null)
throw new ArgumentNullException(nameof (builder));
return builder.UseMiddleware<HttpMethodOverrideMiddleware>(Array.Empty<object>());
}
}
public class HttpMethodOverrideMiddleware
{
private const string xHttpMethodOverride = "X-Http-Method-Override";
private readonly RequestDelegate _next;
private readonly HttpMethodOverrideOptions _options;
public HttpMethodOverrideMiddleware(RequestDelegate next, IOptions<HttpMethodOverrideOptions> options)
{
if (next == null)
throw new ArgumentNullException(nameof (next));
if (options == null)
throw new ArgumentNullException(nameof (options));
this._next = next;
this._options = options.Value;
}
public async Task Invoke(HttpContext context)
{
if (string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
{
if (this._options.FormFieldName != null)
{
if (context.Request.HasFormContentType)
{
StringValues stringValues = (await context.Request.ReadFormAsync(new CancellationToken()))[this._options.FormFieldName];
if (!string.IsNullOrEmpty((string) stringValues))
context.Request.Method = (string) stringValues;
}
}
else
{
StringValues header = context.Request.Headers["X-Http-Method-Override"];
if (!string.IsNullOrEmpty((string) header))
context.Request.Method = (string) header;
}
}
await this._next(context);
}
}
C#:只支持GET和POST方法的浏览器,如何发送PUT/DELETE请求?RESTful WebAPI如何响应?的更多相关文章
- Fiddler使用方法之Fiddler显示IP,Fiddler中文乱码解决方法以及Fiddler模拟发送get/post请求
Fiddler是一个HTTP的调试代理,以代理服务器的方式,监听系统的Http网络数据流动,是我们常用的抓包工具之一 今天为大家分享一下几个使用Fiddler的小技巧 一.Fiddler抓包中文乱码问 ...
- 关于scrollbar-face-color只支持ie的解决办法!
关于scrollbar-face-color只支持ie的解决方法!!今天突然有人问我滚动条css自定义的方法,我发现用scrollbar-base-color这种方法只有ie支持,查了半天资料总结如下 ...
- 解决IE10以下对象不支持“bind”属性或方法
IE10一下的浏览器,如果在JS代码中用了bind函数,那么就会报“SCRIPT438: 对象不支持“bind”属性或方法” 因为浏览器没有提供这个参数的方法,所以我们就自己写一个bind,来让这个参 ...
- 对于方法 String.Contains,只支持可在客户端上求值的参数。
var ProjectLevel_XJJS = "06,07,08,09"; p.Where(e =>ProjectLevel_XJJS.Contains(e.LevelCo ...
- 纯js异步无刷新请求(只支持IE)
纯js异步无刷新请求 下载地址:http://pan.baidu.com/s/1slakL1F 所以因为非IE浏览器都禁止跨域请求,所以以只支持IE. <HTML> <!-- 乱码( ...
- 让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap的解决方法--(转)
如有雷同,不胜荣幸,若转载,请注明 让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap的解决方法 最近做一个Web网站,之前一直觉得bootstrap非常好,这次使用了boot ...
- 基础 - 32位操作系统最多只支持4G内存。
32位操作系统最多只支持4G内存. CPU能不能直接访问硬盘的数据呢, 不能. 只能通过把硬盘的数据先放到内存里, 然后再从内存里访问硬盘的数据.我们平时玩游戏碰上读图loading 进度条的这个过程 ...
- 纯js异步无刷新请求(只支持IE)【原】
纯js异步无刷新请求 下载地址:http://pan.baidu.com/s/1slakL1F 所以因为非IE浏览器都禁止跨域请求,所以以只支持IE. <HTML> <!-- 乱码( ...
- 无法加载ISAPI 筛选器 当前配置只支持加载为 AMD64 处理器体系结构创建的映像
无法加载ISAPI 筛选器 当前配置只支持加载为 AMD64 处理器体系结构创建的映像 2011-11-9 0:18:49来源:本站原创作者:清晨320我要评论(0) 今天服务器的伪静态死活加载不上去 ...
- 转载------让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap的解决方法
本文是转载及收藏 让IE6 IE7 IE8 IE9 IE10 IE11支持Bootstrap的解决方法 最近做一个Web网站,之前一直觉得bootstrap非常好,这次使用了bootstrap3,在c ...
随机推荐
- C# Linq、Lambda表达式树动态构建、合并条件扩展方法
前言 日常开发时,使用Linq和EF经常会在存在多条件查询,或者说动态条件查询时,便存在合并表达式树的情况.基于这种情况结合一些资料,写了个扩展类,代码如下: 代码实现 /// <summary ...
- 使用 Microsoft Edge WebDriver 自动执行和测试 WebView2 应用 Selenium
https://learn.microsoft.com/zh-cn/microsoft-edge/webview2/how-to/webdriver
- ZDOCK3.02安装及注意事项:基于Linux Ubuntu系统操作
cd zdock3.0.2_linux_x64代码mark_sur model2choose.pdb model2choose_m.pdbmark_sur 1bqi2.pdb 1bqi2_m.pdbz ...
- 深入浅出Java异常机制
一次对Java异常机制的理解 近期有一个对接三方接口的任务,在这个过程中用到了许多 try-catch 处理,发现自己对异常处理是一知半解,浅浅研究了一下,记录一下,也帮助小伙伴如何正确使用 try- ...
- SwiftObject 杂记
一.前言 看了一段时间的Swift,慢慢转变了一些对Swift的看法. Swift作为苹果新晋的开发语言.具有模板编程.函数编程.协议多继承.vTable静态绑定.值引用类型区分.Option类型等动 ...
- RTMP推流FLV插入自定义SEI数据总结
一.需求 在RTMP推送的流中添加一个接口,可以添加自定义的数据(一段字节数组). 经过分析,在H264的流中可以通过SEI添加自定义数据,下面是实施的总结 二.实施 1)准备工具 RTMP推流客户端 ...
- 将mnist训练的caffemodel生成动态链接库DLL
在项目程序中经常看到动态链接库,非常好奇,想自己实现一下,于是乎尝试一波.就因为这种好奇,每天都被bug所困扰... 1. 训练caffemodel 在windows环境下搭建caffe无果,转投Ub ...
- 使用vscode编辑c语言
在 Visual Studio Code (VSCode) 中配置 C 语言环境 步骤指南: 一,前期准备(安装扩展,软件包) 安装 C/C++ 扩展 打开 VSCode. 点击左侧边栏的扩展按钮(或 ...
- umount 报错umount: /new_room: target is busy. (In some cases useful info about processes that use the device is found by lsof(8) or fuser(1))
挂载逻辑卷后,尝试更新逻辑卷的文件系统 [root@server lost+found]# umount /new_room/ umount: /new_room: target is busy. ( ...
- react的反向代理
在配置在src文件夹中setupProxy.js文件,并通过npm安装http-proxy-middleware,代理中间件模块 npm i -S http-proxy-middleware 配置反向 ...