运行Spring RestTemplate
,我们遇到了意外行为,使用了相当典型的HttpClientErrorException
; 401错误未转换为ResourceAccessException
。
具体来说,当我们从服务器收到预期的401时,我们会收到一个IOException
包裹Server returned HTTP response code: 401 for URL: ...
,其中包含sun.net.www.protocol.http.HttpURLConnection
消息,该消息在{{1}的内部深处提升}}
我们的模板配置或多或少如此:
public RestTemplate restTemplate() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
// connections per route is not a meaningful limit for us, so set very high
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(10000);
poolingHttpClientConnectionManager.setMaxTotal(100);
CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager).build();
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
我们对客户端的使用如下:
protected <T, U> ResponseEntity<T> request(UserClientAccount account, U body, String url, HttpMethod httpMethod,
ParameterizedTypeReference<T> responseType, boolean retry) {
try {
HttpEntity<U> request = createHttpEntity(body, account.getToken(this));
return getRestTemplate().exchange(url, httpMethod, request, responseType, new HashMap<String, String>());
}
catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
if (retry) {
log.warn("Unauthorized response for {}. Refreshing token to retry...", url);
refreshToken(account);
// tries again
return request(account, null, url, httpMethod, responseType, false);
}
else {
log.error("Unauthorized error calling {}. All attempts to retry exhausted ", url);
throw e;
}
}
throw new ProgramException("Error while performing " + httpMethod + " request to " + url + ". " +
"Response body: " + e.getResponseBodyAsString(), e);
}
}
我们对HttpClientErrorException
的追捕从未被击中;相反,我们收到ResourceAccessException
,原因是上述IOException
。
我们做错了什么?