【问题标题】:Apache HTTP client doesn't respect HTTP status code 307Apache HTTP 客户端不遵守 HTTP 状态代码 307
【发布时间】:2012-09-21 11:23:25
【问题描述】:

当我们的 Android 客户端使用 Apache HTTP 客户端向我们的服务器发出请求时,我希望它使用相同的 HTTP 方法重定向到一个新的 url(或更具体地,另一个上下文路径)。

在我的 httpd.conf 中,我使用状态码 307 设置了这条规则:

Redirect    307    /mybipper/reg           /mybipperapi/old/reg

根据 Wikipedia 中的状态码描述,307 应该:

http://en.wikipedia.org/wiki/HTTP_307#3xx_Redirection

307 临时重定向(从 HTTP/1.1 开始) 在这种情况下,应该使用另一个 URI 重复请求;但是,未来的请求仍然可以使用原始 URI。[2]与历史上 302 的实现方式相比,在重新发出原始请求时不应更改请求方法。例如,必须使用另一个 POST 请求重复一个 POST 请求。

但在我的访问日志中,我们看到 HTTP 客户端似乎不尊重它,而是执行 GET,就好像我返回了状态代码 302

172.29.9.120 - - [21/Sep/2012:14:02:11 +0300] "POST /mybipper/reg HTTP/1.1" 307 248
172.29.9.120 - - [21/Sep/2012:14:02:11 +0300] "GET /mybipperapi/old/reg HTTP/1.1" 400 1016

根据 Apache HTTP 客户端网站,它有点不清楚它应该如何处理状态代码 307,但他们至少在那里列出了它。

http://hc.apache.org/httpclient-3.x/redirects.html

我有一种强烈的感觉,它的 Apache HTTP 客户端没有正确实现 HTTP 1.1 协议,我是对的还是我误解了什么?

我们使用的 Apache HTTP 客户端与 Android SDK 捆绑在一起。我正在测试的手机有 Android SDK 15,所以这个:

http://developer.android.com/about/versions/android-4.0.3.html

【问题讨论】:

  • 你找到解决办法了吗?
  • 不,很遗憾我没有,我不得不做一个解决方法,不记得我现在做了什么,那是很久以前的事了。你有同样的问题吗?我记得我总是不确定我是否遗漏了什么或者它是否是 Apache 中的错误
  • http 规范说,在没有用户确认的情况下,不得重复 get 或 head 以外的请求导致 307。所以我认为http客户端是尊重规范的,你应该提示用户重新提交

标签: apache-httpclient-4.x apache-httpcomponents


【解决方案1】:

DefaultRedirectStrategy 只允许自动重定向 GET 和 HEAD。如果您还想允许 POST(但不允许 PUT 或 DELETE),您可以通过执行以下操作切换到 LaxRedirectStrategy

HttpClientBuilder hcb = HttpClients.custom();
hcb.setRedirectStrategy(new LaxRedirectStrategy());
HttpClient client = hcb.build(); 

如果您还想遵循 PUT 和 DELETE(就像我们在此处所做的那样),则必须实施自定义策略(注意:我们在 HttpClient 中遇到了一个错误,它似乎试图添加第二个内容-Length header 当我们这样做时,所以我们手动删除它。YMMV)。通过使用这种策略,HttpClient 还将支持 308 重定向,这是 Apache 团队甚至懒得包含的。

你可以这样做:

hcb.setRedirectStrategy(new DefaultRedirectStrategy() {
        public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
            Args.notNull(request, "HTTP request");
            Args.notNull(response, "HTTP response");
            int statusCode = response.getStatusLine().getStatusCode();
            switch(statusCode) {
            case 301:
            case 307:
            case 302:
            case 308:
            case 303:
                return true;
            case 304:
            case 305:
            case 306:
            default:
                return false;
            }
        }

        public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
            URI uri = this.getLocationURI(request, response, context);
            String method = request.getRequestLine().getMethod();
            if(method.equalsIgnoreCase("HEAD")) {
                return new HttpHead(uri);
            } else if(method.equalsIgnoreCase("GET")) {
                return new HttpGet(uri);
            } else {
                int status = response.getStatusLine().getStatusCode();
                HttpUriRequest toReturn = null;
                if(status == 307 || status == 308) {
                    toReturn = RequestBuilder.copy(request).setUri(uri).build();
                    toReturn.removeHeaders("Content-Length"); //Workaround for an apparent bug in HttpClient
                } else {
                    toReturn = new HttpGet(uri);
                }
                return toReturn;
            }
        }
    });

【讨论】:

    【解决方案2】:

    要扩展 Cody 的正确答案 - 如果您需要遵循 PUT(或任何其他方法)307 重定向,那么您也可以扩展 LaxRedirectStrategy,这更容易:

    hcb.setRedirectStrategy(new LaxRedirectStrategy()
    {
        protected boolean isRedirectable(String method)
        {
            return "PUT".equalsIgnoreCase(method)||super.isRedirectable(method);
        }
    });
    

    但是,这也不能解决跟随 308 的问题。我知道这是一个老问题,但我今天遇到了同样的问题(感谢 Cody)。

    【讨论】:

      【解决方案3】:

      如果您也想将 308 添加到 LaxRedirectStrategy - 请参阅下面的代码

      .setRedirectStrategy(new LaxRedirectStrategy() {
                @Override
                   public boolean isRedirected(
                           final HttpRequest request,
                           final HttpResponse response,
                           final HttpContext context) throws ProtocolException {
                       Args.notNull(request, "HTTP request");
                       Args.notNull(response, "HTTP response");
      
                       final int statusCode = response.getStatusLine().getStatusCode();
                       final String method = request.getRequestLine().getMethod();
                       final Header locationHeader = response.getFirstHeader("location");
                       switch (statusCode) {
                       case HttpStatus.SC_MOVED_TEMPORARILY:
                           return isRedirectable(method) && locationHeader != null;
                       case HttpStatus.SC_MOVED_PERMANENTLY:
                       case HttpStatus.SC_TEMPORARY_REDIRECT:
                           return isRedirectable(method);
                       case HttpStatus.SC_SEE_OTHER:
                           return true;
                       case 308:
                           return true;
                       default:
                           return false;
                       } //end of switch
                   }
           })
                   .build();```
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-13
        • 1970-01-01
        • 2011-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多