目录
1. 问题描述
在开放的api接口中,部分系统会使用业务状态来表示请求的响应状态,比如当查找的资源不存在时,返回的Response code为404,此时使用spring-web封装的RestTemplate,会抛出异常,不利于后续的逻辑处理;
2. 背景
对接客户的系统时,使用的时spring-web的RestTemplate,当请求的结果不是预期正确的结果时,网络请求会直接抛出异常,不利于后续的业务处理;
3. 原因
spring-web封装的RestTemplate中,错误处理器默认使用的是DefaultResponseErrorHandler,当响应状态为4XX,5XX时,hasError方法会返回true,handleError方法会抛出对应的异常;
3.1 RestTemplate 类
3.1.1 doExecute
@Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
// 处理响应
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
3.1.2 handleResponse
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
ResponseErrorHandler errorHandler = getErrorHandler();
// 判断是否有错误
boolean hasError = errorHandler.hasError(response);
if (logger.isDebugEnabled()) {
try {
int code = response.getRawStatusCode();
HttpStatus status = HttpStatus.resolve(code);
logger.debug("Response " + (status != null ? status : code));
}
catch (IOException ex) {
// ignore
}
}
// 如果有错误,处理错误
if (hasError) {
errorHandler.handleError(url, method, response);
}
}
3.2 DefaultResponseErrorHandler类
3.2.1 hasError
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
int rawStatusCode = response.getRawStatusCode();
HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
return (statusCode != null ? hasError(statusCode) : hasError(rawStatusCode));
}
protected boolean hasError(HttpStatus statusCode) {
return statusCode.isError();
}
3.2.2 handleError
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
if (statusCode == null) {
byte[] body = getResponseBody(response);
String message = getErrorMessage(response.getRawStatusCode(),
response.getStatusText(), body, getCharset(response));
throw new UnknownHttpStatusCodeException(message,
response.getRawStatusCode(), response.getStatusText(),
response.getHeaders(), body, getCharset(response));
}
handleError(response, statusCode);
}
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
String statusText = response.getStatusText();
HttpHeaders headers = response.getHeaders();
byte[] body = getResponseBody(response);
Charset charset = getCharset(response);
String message = getErrorMessage(statusCode.value(), statusText, body, charset);
switch (statusCode.series()) {
case CLIENT_ERROR:
throw HttpClientErrorException.create(message, statusCode, statusText, headers, body, charset);
case SERVER_ERROR:
throw HttpServerErrorException.create(message, statusCode, statusText, headers, body, charset);
default:
throw new UnknownHttpStatusCodeException(message, statusCode.value(), statusText, headers, body, charset);
}
}
4. 解决方法
4.1 使用自定的错误处理器
自定义的错误处理器需要实现
ResponseErrorHandler
类
4.2 代码
private @Resource Environment environment;
//http请求配置
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setReadTimeout(environment.getProperty("client.http.request.readTimeout", Integer.class, 15000));
requestFactory.setConnectTimeout(environment.getProperty("client.http.request.connectTimeout", Integer.class, 3000));
RestTemplate rt = new RestTemplate(requestFactory);
// 设置自定义的错误处理器,注意,此时没有处理,在使用结果前应处理错误情况
rt.setErrorHandler(new ResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return false;
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
});
return rt;
}
5. 小结
笔记记录