java.net.HttpRetryException:由于服务器身份验证,在流式处理模式下无法重试

2022-09-01 04:20:53

我们的应用程序中有两个部分:
服务器 - 提供REST服务
客户端 - 通过Spring restTemplate使用它们

除了HTTP状态之外,我们的服务器还返回一个带有JSON的HTTP正文,详细说明错误。因此,我已将自定义错误处理程序添加到 restTemplate 中,以将一些编码为非错误的错误处理程序 - 它有助于很好地解析 HTTP 正文。

但是,在HTTP / 1.1 401 Unauthorized的情况下,通过解析HTTP正文,我得到了一个异常。所有其他错误代码处理正常(400、402 等)。我们使用的是普通服务器逻辑,在发生错误时发送HTTP响应,对于不同类型的错误没有特殊规则:

writeErrorToResponse(int status, String errMsg, HttpServletResponse resp) throws IOException {
        response.setStatus(status);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        String message = String.format("{\"error\":\"%s\"}", StringUtils.escapeJson(errMsg));
        resp.getWriter().println(message);
    }

但在客户端上,只有 HTTP/1.1 401 会引发异常 -"java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode"

我做了一些调试,看到问题的原因是SimpleClientHttpResponse中的代码:

HttpURLConnection.getInputStream()

使用 Fiddler 进行跟踪具有以下后续响应:消息在客户端上解析正确:

HTTP/1.1 402 Payment Required
X-Powered-By: Servlet/3.0
Content-Type: application/json
Content-Language: en-GB
Content-Length: 55
Connection: Close
Date: Sat, 25 May 2013 10:10:44 GMT
Server: WebSphere Application Server/8.0

{"error":"I cant find that user.  Please try again."}

以及导致异常的消息:

HTTP/1.1 401 Unauthorized
X-Powered-By: Servlet/3.0
Content-Type: application/json
Content-Language: en-GB
Content-Length: 55
Date: Sat, 25 May 2013 11:00:21 GMT
Server: WebSphere Application Server/8.0

{"error":"I cant find that user.  Please try again."}

在这种情况下,java.net.HttpRetryException的原因可能是什么?

另外:前段时间这个机制工作正常。但是由于我们已经在应用程序中更改了很多代码。


答案 1

我在使用SimpleClientHttpRequestFactory时遇到了同样的问题。通过设置解决它

SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setOutputStreaming(false);
return requestFactory;

该问题是由于分块和随后的重试机制,以防身份验证。

您还可以使用 HttpClientPolicy 禁用块


答案 2

此问题在春季框架中进行了报告。

参考资料https://jira.spring.io/browse/SPR-9367

从引用复制:使用默认设置在 RestTemplate 中处理 401 响应非常困难(不可能)。事实上,这是可能的,但您必须提供错误处理程序和请求工厂。错误处理程序是显而易见的,但问题是默认请求工厂使用 java.net 当您尝试查看响应的状态代码时,它可能会抛出HttpRetryException(尽管它显然可用)。解决方案是使用HttpComponentsClientHttpRequestFactory。例如:

template.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
template.setErrorHandler(new DefaultResponseErrorHandler() {
    public boolean hasError(ClientHttpResponse response) throws IOException {
        HttpStatus statusCode = response.getStatusCode();
        return statusCode.series() == HttpStatus.Series.SERVER_ERROR;
    }
});

HttpComponentsClientHttpRequestFactory需要更低的依赖性。在 POM 文件中添加以下依赖项:

<dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
</dependency>

推荐