RestTemplate处理HTTP状态码为400、500等错误码时,如何获取到其响应结果内容
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
一、问题的出现
最近在使用RestTemplate发送HTTP请求时服务端发送回来的响应结果中HTTP响应码为400、500之类的RestTemplate默认不处理这类响应结果直接抛异常。但是该请求的响应结果内容却是我需要用到的因为我需要通过该请求的响应结果内容告诉用户远程调用接口时出现错误时问题是什么以及通过请求返回的自定义结果集来进行其他操作。
现在我们开发中不管用户执行该请求是否成功返回的HTTP状态码都是200但是会在返回的自定义结果集中的code、message去体现具体操作成功与否。
但是该远程服务器返回的结果不太一样如果用户的操作失败首先返回的HTTP状态码是400之类的但是返回的结果是自定义的结果集。
这就导致了如果不对RestTemplate进行任何配置的话RestTemplate在遇到HTTP状态码为400、500之类的状态码直接就抛异常了就算请求有返回的结果集我也拿不到.
二、解决方案
给RestTemplate设置一个自定义的ResponeErrorHandler
/**
* RestTemplate配置类
*/
@Slf4j
@Configuration
public class RestTemplateConfig {
/**
* 常用远程调用RestTemplate
* @return restTemplate
*/
@Bean("restTemplate")
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new AcceptResponseErrorHandler());
return restTemplate ;
}
/**
* 使RestTemplate能够对响应的错误消息不进行处理
* 如当响应码为400、500等错误时能够不进行处理最终用户可以获取到body数据
*/
private static class AcceptResponseErrorHandler implements ResponseErrorHandler {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return false;
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
}
至此通过以上配置RestTemplate在遇到HTTP状态码为400、500错误码时能够不抛出异常开发者也能够对其中的响应结果进行处理。
以下将简单地对源码进行分析展现为什么RestTemplate在遇到HTTP状态码为400、500错误码时会抛出异常。
三、源码分析
1、在RestTemplate中给定了一个默认的ResponseErrorHandler也就是在该类中对返回的错误码进行了处理。
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
2、不管是调动get、post或者execute方法最终都是调用到了doExecute方法。
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、由于RestTemplate是对响应的错误码进行了拦截那么我们将重点放到了doExecute方法中的以下方法。
handleResponse(url, method, response);
该方法对响应结果进行了处理那么ResponseErrorHandler就在该方法里起了作用。
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
// 拿到了ResponseErrorHandler如果我们有给RestTemaplate指定就拿到我们的
// 没有指定的话就拿到默认的也就是DefaultResponseErrorHandler
ResponseErrorHandler errorHandler = getErrorHandler();
// 在该处通过调用ResponseErrorHandler 判断是否有错如果有的话就进入handleError
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) {
// 调用handleError去处理错误
errorHandler.handleError(url, method, response);
}
}
通过以上代码分析可以知道是通过调用了ResponseErrorHandler的hasError函数去判断结果中是否有错误然后通过调用其handleError函数取处理错误。
4、由于我们没有对RestTemplate进行过任何ResponseErrorHandler的设置那么我们直接进行它默认的DefaultResponseErrorHandler去分析其hasError和handleError函数。
5、首先观察下DefaultResponseErrorHandler的hasError看其是如何判断接口的返回是错误的。
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
int rawStatusCode = response.getRawStatusCode();
// 对响应码进行处理可以不管
HttpStatus statusCode = HttpStatus.resolve(rawStatusCode);
// 核心是重载的hasError函数
return (statusCode != null ? hasError(statusCode) : hasError(rawStatusCode));
}
通过以上函数入口得知重载的hasError是关键入口。
protected boolean hasError(HttpStatus statusCode) {
return statusCode.isError();
}
/**
* Template method called from {@link #hasError(ClientHttpResponse)}.
* <p>The default implementation checks if the given status code is
* {@code HttpStatus.Series#CLIENT_ERROR CLIENT_ERROR} or
* {@code HttpStatus.Series#SERVER_ERROR SERVER_ERROR}.
* Can be overridden in subclasses.
* @param unknownStatusCode the HTTP status code as raw value
* @return {@code true} if the response indicates an error; {@code false} otherwise
* @since 4.3.21
* @see HttpStatus.Series#CLIENT_ERROR
* @see HttpStatus.Series#SERVER_ERROR
*/
protected boolean hasError(int unknownStatusCode) {
HttpStatus.Series series = HttpStatus.Series.resolve(unknownStatusCode);
return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
}
可以看到第二个hasError函数当对应的响应码为Client_Error4xx或者是Server_Error5xx时就断定该响应结果是有错误的。
6、当得知4xx、5xx的响应码断定为请求响应式错误的那么接下来则进入到了DefaultResponseErrorHandler的handleError函数。
@Override
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = getHttpStatusCode(response);
switch (statusCode.series()) {
case CLIENT_ERROR:
throw new HttpClientErrorException(statusCode, response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
case SERVER_ERROR:
throw new HttpServerErrorException(statusCode, response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
default:
throw new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(),
response.getHeaders(), getResponseBody(response), getCharset(response));
}
}
可以看到当错误码为Client_Error4xx或者是Server_Error5xx时都是抛出异常既然是在此处抛出异常那么RestTemplate在调用接口之后遇到此类错误码自然也就抛出异常不再进行下一步获取数据了用户拿不到
7、最终只要我们实现一个ResponseErrorHandlerhasError始终返回falsehandleError不做任何处理那么我们能够顺利拿到当响应码为4xx、5xx之类时的响应数据了。