11月13, 2021

HTTP中间层不支持PUT/DELETE等特定METHOD时的处理

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.

本文链接:https://587v5.com/post/HTTP-zhong-jian-ceng-bu-zhi-chi-PUT/DELETE-deng-te-ding-METHOD-shi-de-chu-li.html

Comments