java.net.HttpRetryException:由于服务器身份验证而无法重试,在流模式下
Posted
技术标签:
【中文标题】java.net.HttpRetryException:由于服务器身份验证而无法重试,在流模式下【英文标题】:java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode 【发布时间】:2013-05-20 21:09:10 【问题描述】:我们的应用中有两个部分: 服务器 - 提供 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)在自定义错误处理程序中添加以将 403 处理为错误。 2)捕获抛出的异常并处理403错误代码。但在这种情况下无法解析 HTTP 正文。 @user1182217 看我上面的回答 有人已经在 Spring Framework 中提出了问题。但是它已经关闭了,我正在重新打开它。请为它投票:jira.spring.io/browse/SPR-16781 【参考方案1】:我在使用SimpleClientHttpRequestFactory 时遇到了同样的问题。通过设置解决了
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setOutputStreaming(false);
return requestFactory;
问题是由于在身份验证时分块和后续重试机制造成的。
您还可以使用 HttpClientPolicy 禁用块
【讨论】:
【参考方案2】:Spring Framework 中报告了此问题。
参考: 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>
【讨论】:
非常感谢您!这是我可以设法测试受“基本身份验证”保护的休息点的唯一方法。 救命稻草,不知道自己应该如何解决这个问题?【参考方案3】:我在使用 Spring Boot 2.2.4 和 TestRestTemplate 时遇到了完全相同的问题。它仅在 401 上失败。只需在测试范围内添加最新版本的Http Client (4.5.11) 即可解决问题。无需进一步配置。
【讨论】:
【参考方案4】:我认为将配置的 HttpComponentsClientHttpRequestFactory 传递给您的 RestTemplate 会有所帮助。 Here你可能会找到解决方法。
我之所以使用这样的开发方案,是为了处理401、404等Http错误响应体中的响应消息,也为了让应用部署在真实服务器端环境。
【讨论】:
【参考方案5】:此异常对 Spring OAuth2 令牌检索有相当意想不到的影响。对于此检索,标准实现使用RestTemplate
,它配置有SimpleClientHttpRequestFactory
,其中输出流设置为true。如果发生 4XX 身份验证错误,标准 ResponseErrorHandler
会尝试读取输出流并接收当然讨论的异常,但决定静默使用它,忽略。
然而,有时 OAuth2 身份验证服务器会将重要信息写入必须读取和分析的输出/错误流中。
为了使这些信息可用,在创建OAuth2RestTemplate
时,其AccessTokenProvider
应设置为自定义,我们将其命名为ErrorStreamAwareAccessTokenProvider
:
oauth2RestTemplate.setAccessTokenProvider(new ErrorStreamAwareAccessTokenProvider());
这个自定义AccessTokenProvider
应该覆盖,例如getRestTemplate
方法并相应地调整 RestTemplate
,作为接受的答案建议。例如:
class ErrorStreamAwareAccessTokenProvider extends OAuth2AccessTokenSupport
// your exact superclass may vary depending on grant type and other things
protected RestOperations getRestTemplate()
RestTemplate template = super.getRestTemplate();
template.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
// ... also set ResponseErrorHandler if needed
return template;
完成此操作后,标准 ResponseErrorHandler
可以读取输出/错误流,或者,例如,如果您想抛出一个自定义异常,其中存储了检索到的信息,您可以设置您的自定义 @987654333上面覆盖方法中的@。
【讨论】:
以上是关于java.net.HttpRetryException:由于服务器身份验证而无法重试,在流模式下的主要内容,如果未能解决你的问题,请参考以下文章