关注wx:CodingTechWork
引言
在代码开发过程中,发现很多地方通过RestTemplate
调用了第三方接口,而第三方接口需要根据某些状态码或者异常进行重试调用,此时,要么在每个调用的地方进行异常捕获,然后重试;要么在封装的RestTemplate
工具类中进行统一异常捕获和封装。当然,本文不走寻常路,将会通过RestTemplate
的异常处理器进行操作。
RestTemplate异常处理器介绍
分类
异常处理器 | 功能描述 |
---|---|
ResponseErrorHandler | 异常处理器接口,是restTemplate所有异常处理器的实现接口 |
DefaultResponseErrorHandler | 默认的异常处理器,处理客户端和服务端异常 |
ExtractingResponseErrorHandler | 将HTTP错误响应转换RestClientException |
NoOpResponseErrorHandler | 不处理异常 |
RestTemplate异常处理器源码
ResponseErrorHandler
java
public interface ResponseErrorHandler {
/**
* 判断请求是否异常
* false: 请求返回无错误
* true: 请求返回有错误
* 可定制化根据某一些status的值进行返回,如根据2xx返回false,非2xx返回true
* 同时,可根据ClientHttpResponse的返回结果来定制化判断
*/
boolean hasError(ClientHttpResponse var1) throws IOException;
/**
* 处理错误
*/
void handleError(ClientHttpResponse var1) throws IOException;
/**
* 默认的异常处理方法
*/
default void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
//默认调用handleError(response)方法,也可以重写该方法
this.handleError(response);
}
}
DefaultResponseErrorHandler
java
package org.springframework.web.client;
import java.io.IOException;
import java.nio.charset.Charset;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.FileCopyUtils;
public class DefaultResponseErrorHandler implements ResponseErrorHandler {
public DefaultResponseErrorHandler() {
}
/**
* 判断请求是否异常
*/
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
//statusCode不为空并调用受保护的方法hasError()方法
return statusCode != null && this.hasError(statusCode);
}
protected boolean hasError(HttpStatus statusCode) {
//遇到客户端错误4xx或服务端错误5xx,就返回true表示有错误
return statusCode.series() == Series.CLIENT_ERROR || statusCode.series() == Series.SERVER_ERROR;
}
/**
* 处理错误
*/
public void handleError(ClientHttpResponse response) throws IOException {
//获取状态码
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
if (statusCode == null) {
throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
} else {
//状态码不为空,则处理错误(主要是4xx和5xx错误)
this.handleError(response, statusCode);
}
}
/**
* 处理错误
*/
protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
String statusText = response.getStatusText();
HttpHeaders headers = response.getHeaders();
byte[] body = this.getResponseBody(response);
Charset charset = this.getCharset(response);
switch(statusCode.series()) {
case CLIENT_ERROR:
//http客户端错误
throw HttpClientErrorException.create(statusCode, statusText, headers, body, charset);
case SERVER_ERROR:
//http服务端错误
throw HttpServerErrorException.create(statusCode, statusText, headers, body, charset);
default:
throw new UnknownHttpStatusCodeException(statusCode.value(), statusText, headers, body, charset);
}
}
/** @deprecated */
@Deprecated
protected HttpStatus getHttpStatusCode(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
if (statusCode == null) {
throw new UnknownHttpStatusCodeException(response.getRawStatusCode(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
} else {
return statusCode;
}
}
protected byte[] getResponseBody(ClientHttpResponse response) {
try {
return FileCopyUtils.copyToByteArray(response.getBody());
} catch (IOException var3) {
return new byte[0];
}
}
@Nullable
protected Charset getCharset(ClientHttpResponse response) {
HttpHeaders headers = response.getHeaders();
MediaType contentType = headers.getContentType();
return contentType != null ? contentType.getCharset() : null;
}
}
ExtractingResponseErrorHandler
java
package org.springframework.web.client;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatus.Series;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
public class ExtractingResponseErrorHandler extends DefaultResponseErrorHandler {
//定义HttpMessageConverter对象列表
private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();
private final Map<HttpStatus, Class<? extends RestClientException>> statusMapping = new LinkedHashMap();
private final Map<Series, Class<? extends RestClientException>> seriesMapping = new LinkedHashMap();
public ExtractingResponseErrorHandler() {
}
public ExtractingResponseErrorHandler(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = messageConverters;
}
public void setStatusMapping(Map<HttpStatus, Class<? extends RestClientException>> statusMapping) {
if (!CollectionUtils.isEmpty(statusMapping)) {
this.statusMapping.putAll(statusMapping);
}
}
public void setSeriesMapping(Map<Series, Class<? extends RestClientException>> seriesMapping) {
if (!CollectionUtils.isEmpty(seriesMapping)) {
this.seriesMapping.putAll(seriesMapping);
}
}
protected boolean hasError(HttpStatus statusCode) {
if (this.statusMapping.containsKey(statusCode)) {
return this.statusMapping.get(statusCode) != null;
} else if (this.seriesMapping.containsKey(statusCode.series())) {
return this.seriesMapping.get(statusCode.series()) != null;
} else {
return super.hasError(statusCode);
}
}
public void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
if (this.statusMapping.containsKey(statusCode)) {
this.extract((Class)this.statusMapping.get(statusCode), response);
} else if (this.seriesMapping.containsKey(statusCode.series())) {
this.extract((Class)this.seriesMapping.get(statusCode.series()), response);
} else {
super.handleError(response, statusCode);
}
}
//转换抽取为RestClientException异常
private void extract(@Nullable Class<? extends RestClientException> exceptionClass, ClientHttpResponse response) throws IOException {
if (exceptionClass != null) {
HttpMessageConverterExtractor<? extends RestClientException> extractor = new HttpMessageConverterExtractor(exceptionClass, this.messageConverters);
RestClientException exception = (RestClientException)extractor.extractData(response);
if (exception != null) {
throw exception;
}
}
}
}
NoOpResponseErrorHandler
java
//在TestRestTemplate类中
private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {
private NoOpResponseErrorHandler() {
}
//不做错误处理
public void handleError(ClientHttpResponse response) throws IOException {
}
}
RestTemplate异常处理器被触发源码
- 初始化errorHandler变量
- 执行doExecute()方法
java
@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;
Object var14;
try {
ClientHttpRequest request = this.createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
//处理响应
this.handleResponse(url, method, response);
var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;
} catch (IOException var12) {
String resource = url.toString();
String query = url.getRawQuery();
resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;
throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + var12.getMessage(), var12);
} finally {
if (response != null) {
response.close();
}
}
return var14;
}
- 处理响应,调用handleResponse()方法
java
protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
//获取异常处理器
ResponseErrorHandler errorHandler = this.getErrorHandler();
boolean hasError = errorHandler.hasError(response);
if (this.logger.isDebugEnabled()) {
try {
int code = response.getRawStatusCode();
HttpStatus status = HttpStatus.resolve(code);
this.logger.debug("Response " + (status != null ? status : code));
} catch (IOException var8) {
}
}
if (hasError) {
errorHandler.handleError(url, method, response);
}
}
- 获取异常处理器,调用getErrorHandler()方法
java
public ResponseErrorHandler getErrorHandler() {
//返回的就是RestTemplate中的成员变量errorHandler
return this.errorHandler;
}
RestTemplate异常处理器实践模板
定义一个自定义的errorHandler实现ResponseErrorHandler接口
- errorHandler
java
/**
* 继承默认错误处理器DefaultResponseErrorHandler,无需关注hasError和handlerError方法
*/
@Component
public class MyResponseErrorHandler implements ResponseErrorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);
/**
* my service进行定制化处理
*/
@Autowired
private MyService myService;
@Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return clientHttpResponse.getStatusCode().value() != 200 && clientHttpResponse.getStatusCode().value() != 201 && clientHttpResponse.getStatusCode().value() !=302;
}
@Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
//遇到401进行单独处理
if (HttpStatus.UNAUTHORIZED.value() == response.getStatusCode().value()) {
myService.doSomething(url.getHost());
} else {
//继续抛异常
throw new RuntimeException(clientHttpResponse.getStatusText());
}
}
@Override
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
LOGGER.error("=======================ERROR HANDLER============================");
LOGGER.error("DateTime:{}", PoolUtil.currentTime());
LOGGER.error("HOST:{},URI:{}", url.getHost(), url.getPath());
LOGGER.error("Method:{}", method.name());
LOGGER.error("Exception:{}", response.getStatusCode());
LOGGER.error("StatusText: {}", response.getStatusText());
LOGGER.error("========================================================");
}
}
- HttpClientUtils工具类
java
@Slf4j
public class HttpClientUtils {
//http协议
private static final String HTTP_PROTOCOL = "http";
//https协议
private static final String HTTPS_PROTOCAL = "https";
//最大连接数
private static final int MAX_CONNECT = 300;
//默认连接数
private static final int DEFAULT_CONNECT = 200;
public HttpClientUtils(){
}
/**
* new一个http client
*/
public static CloseableHttpClient newHttpClientForHttpsOrHttp() {
HttpClientBuilder build = HttpClientBuilder.create();
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] arg0, String arg1){
return true;
}
}).build();
build.setSslcontext(sslContext);
//X509HostnameVerifier校验
X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() {
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
public void verify(String arg0, SSLSocket arg1){}
public void verify(String arg0, String[] arg1, String[] arg2){}
public void verify(String arg0, X509Certificate arg1){}
};
//SSL连接socket工厂
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
//http和https协议register
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
.register(HTTP_PROTOCOL, PlainConnectionSocketFactory.getSocketFactory()).register(HTTPS_PROTOCAL, sslSocketFactory)
.build();
//连接池
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connMgr.setDefaultMaxPerRoute(DEFAULT_CONNECT_NUM);
connMgr.setMaxTotal(MAX_CONNECT_NUM);
build.setConnectionManager(poolingHttpClientConnectionManager);
//构建CloseableHttpClient
CloseableHttpClient closeableHttpClient = build.build();
return closeableHttpClient;
} catch (Exception e ) {
log.error("异常:{}", e.getLocalizedMessage());
}
return null;
}
}
- restTemplate调用
java
/**
* 获取远程连接template
*
* @return
*/
public static RestTemplate getRestTempte() {
try {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(HttpClientUtils.newHttpClientForHttpsOrHttp());
factory.setConnectTimeout(30000);
//设置handler
return new RestTemplate(factory).setErrorHandler(new MyResponseErrorHandler());
} catch (Exception e) {
return null;
}
}
定义一个自定义的errorHandler继承DefaultResponseErrorHandler类
java
/**
* 继承默认错误处理器DefaultResponseErrorHandler,无需关注hasError和handlerError方法
*/
@Component
public class MyResponseErrorHandler extends DefaultResponseErrorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);
/**
* my service进行定制化处理
*/
@Autowired
private MyService myService;
@Override
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
LOGGER.error("=======================ERROR HANDLER============================");
LOGGER.error("DateTime:{}", PoolUtil.currentTime());
LOGGER.error("HOST:{},URI:{}", url.getHost(), url.getPath());
LOGGER.error("Method:{}", method.name());
LOGGER.error("Exception:{}", response.getStatusCode());
LOGGER.error("StatusText: {}", response.getStatusText());
LOGGER.error("========================================================");
//遇到401进行单独处理
if (HttpStatus.UNAUTHORIZED.value() == response.getStatusCode().value()) {
myService.doSomething(url.getHost());
} else {
this.handleError(response);
}
}
}