HTTP中间层不支持PUT/DELETE等特定METHOD时的处理
昨天,有兄弟跑过来找我,说部署在属地客户服务器上的代码,由于客户服务器安全限制,只支持HTTP协议里的GET和POST请求,不支持PUT/DELETE请求!?!? 我们的很多线上服务都已经是按restful设计,有PUT和DELETE的api,总不可能让这些服务都为了适应某个客户,适配一堆POST的api吧?这个工作量不说,后续的维护以及新功能开发,都存在麻烦!暂时不考虑这种方案!
这时,一个同事说有一个扩展属性:X-HTTP-Method-Override,它是一个非标准的HTTP协议头,是约定俗成的一个请求头字段,用于规避上面这种问题,举个例子:
一个正常的请求是:
DELETE https://587v5.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 https://587v5.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;
server_name 587v5.com;
access_log /data/logs/nginx/587v5.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/www/root/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"
ps: 我公司的兄弟,在实际上线中,还碰到了别的问题,原因是他把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.
Comments