鉴于很多兄弟有问题,特意在文章顶部小结一下nginx使用:
- nginx里配置,在server配置里,或location的配置里,添加如下配置,并重启nginx:
set $method $request_method;
proxy_set_header X-Old-Method $request_method; # 测试用
if ($http_X_HTTP_Method_Override ~* 'PUT|DELETE') { # 也可以改成if ($http_X_HTTP_Method_Override) 这是强制替换
set $method $http_X_HTTP_Method_Override;
}
proxy_method $method;
- 在请求的header里添加
X-HTTP-Method-Override: PUT
- 注意:
- nginx配置使用下划线
$http_X_HTTP_Method_Override
,请求使用横杠X-HTTP-Method-Override
- nginx默认会忽略带下划线的header名,所以请求的header名如果带下划线,nginx是收不到的
- 按rfc标准,header名忽略大小写,因此
X-HTTP-Method-Override
等同于x-http-method-override
- 注:header的值是大小写敏感的,url也是大小写敏感的,METHOD是必须大写的
- nginx配置使用下划线
原文如下:
昨天,有兄弟跑过来找我,说正在开发的支付宝小程序,只支持HTTP协议里的GET和POST请求,不支持PUT/DELETE请求!?!?
我们的很多线上服务都已经是按restful设计,有PUT和DELETE的api,总不可能让这些服务都为了支付宝,适配一堆POST的api吧?这个工作量不说,后续的维护以及新功能开发,都存在麻烦!暂时不考虑这种方案!
这时,一个同事说有一个扩展属性:X-HTTP-Method-Override,它是一个非标准的HTTP协议头,是约定俗成的一个请求头字段,用于规避上面这种问题,举个例子:
一个正常的请求是:
DELETE http://cc.beinet.com/api/code HTTP/1.1
Host: cc.beinet.com
Content-Length: 6
User-Agent: Mozilla/5.0
Content-Type: application/json
para=1
我们要改成===注意第一行变成了POST:
POST http://cc.beinet.com/api/code HTTP/1.1
Host: cc.beinet.com
Content-Length: 6
User-Agent: Mozilla/5.0
Content-Type: application/json
X-HTTP-Method-Override: DELETE
para=1
OK, 为绕过支付宝的问题,我们需要做如下2步:
1、前端必须改代码,这个无法绕过,所有的PUT/DELETE请求,都要求改成POST;
所有的PUT请求,要携带Header:X-HTTP-Method-Override: PUT
所有的DELETE请求,要携带Header:X-HTTP-Method-Override: DELETE
注:有兄弟问我,能不能不用POST,改用GET,答案是不可以,因为在RFC2616-HTTP协议里,不建议在GET请求带上body数据,很多的HTTP服务也不支持GET请求带body,比如google就会报400错误,测试命令:
curl -X GET https://www.google.com -d "{Id: 0}"
2、服务端根据Header:X-HTTP-Method-Override,转换请求METHOD
2.1、在nginx层修改和转发,服务端不需要做任何处理即可,参考配置:
server {
listen 443 ssl http2;
server_name cc.beinet.com;
access_log /data/logs/nginx/cc.beinet.com.log main;
# 在你的nginx配置里,添加下面5行,记得执行 nginx -s reload 重新加载
set $method $request_method;
if ($http_X_HTTP_Method_Override ~* 'PUT|DELETE') {
set $method $http_X_HTTP_Method_Override;
}
proxy_method $method;
location / {
root /data/wwwroot/html;
}
2.2、如果你们没有nginx,那只能修改服务端代码了,Asp.net WebApi参考修改方案: https://www.hanselman.com/blog/HTTPPUTOrDELETENotAllowedUseXHTTPMethodOverrideForYourRESTServiceWithASPNETWebAPI.aspx
// 项目中添加这个类
public class MethodOverrideHandler : DelegatingHandler
{
readonly string[] _methods = { "DELETE", "HEAD", "PUT" };
const string _header = "X-HTTP-Method-Override";
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Post && request.Headers.Contains(_header))
{
var method = request.Headers.GetValues(_header).FirstOrDefault();
if (_methods.Contains(method))
{
request.Method = new HttpMethod(method);
}
}
return base.SendAsync(request, cancellationToken);
}
}
// 然后在你的WebApi项目的global.asax.cs里添加如下代码:
protected void Application_Start()
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new MethodOverrideHandler());
好了,服务端的2个方案,你任意使用其中一个,即可解决问题,测试方法,可以用curl验证,用如下2个命令,如果返回结果一致,说明修改成功生效:
curl -X PUT -H "Content-Type: application/json" -d "{Id: 0}" http://你的域名/api/xxx
curl -X POST -H "Content-Type: application/json" -d "{Id: 0}" http://你的域名/api/xxx -H "X-HTTP-Method-Override: PUT"
btw: 我公司的兄弟,在实际上线中,还碰到了别的问题,原因是他把POST写成小写的post了,这是不符合http协议标准的,标准里强制所有METHOD必须大写,参考:
https://tools.ietf.org/html/rfc2616#section-5.1.1
5.1.1 Method
The Method token indicates the method to be performed on the
resource identified by the Request-URI. The method is case-sensitive.