關(guān)注wx:CodingTechWork
引言
??在代碼開發(fā)過程中,發(fā)現(xiàn)很多地方通過RestTemplate
調(diào)用了第三方接口,而第三方接口需要根據(jù)某些狀態(tài)碼或者異常進(jìn)行重試調(diào)用,此時(shí),要么在每個(gè)調(diào)用的地方進(jìn)行異常捕獲,然后重試;要么在封裝的RestTemplate
工具類中進(jìn)行統(tǒng)一異常捕獲和封裝。當(dāng)然,本文不走尋常路,將會(huì)通過RestTemplate
的異常處理器進(jìn)行操作。文章來源地址http://www.zghlxwxcb.cn/news/detail-654719.html
RestTemplate異常處理器介紹
分類
異常處理器 | 功能描述 |
---|---|
ResponseErrorHandler | 異常處理器接口,是restTemplate所有異常處理器的實(shí)現(xiàn)接口 |
DefaultResponseErrorHandler | 默認(rèn)的異常處理器,處理客戶端和服務(wù)端異常 |
ExtractingResponseErrorHandler | 將HTTP錯(cuò)誤響應(yīng)轉(zhuǎn)換RestClientException |
NoOpResponseErrorHandler | 不處理異常 |
RestTemplate異常處理器源碼
ResponseErrorHandler
public interface ResponseErrorHandler {
/**
* 判斷請(qǐng)求是否異常
* false: 請(qǐng)求返回?zé)o錯(cuò)誤
* true: 請(qǐng)求返回有錯(cuò)誤
* 可定制化根據(jù)某一些status的值進(jìn)行返回,如根據(jù)2xx返回false,非2xx返回true
* 同時(shí),可根據(jù)ClientHttpResponse的返回結(jié)果來定制化判斷
*/
boolean hasError(ClientHttpResponse var1) throws IOException;
/**
* 處理錯(cuò)誤
*/
void handleError(ClientHttpResponse var1) throws IOException;
/**
* 默認(rèn)的異常處理方法
*/
default void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
//默認(rèn)調(diào)用handleError(response)方法,也可以重寫該方法
this.handleError(response);
}
}
DefaultResponseErrorHandler
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() {
}
/**
* 判斷請(qǐng)求是否異常
*/
public boolean hasError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
//statusCode不為空并調(diào)用受保護(hù)的方法hasError()方法
return statusCode != null && this.hasError(statusCode);
}
protected boolean hasError(HttpStatus statusCode) {
//遇到客戶端錯(cuò)誤4xx或服務(wù)端錯(cuò)誤5xx,就返回true表示有錯(cuò)誤
return statusCode.series() == Series.CLIENT_ERROR || statusCode.series() == Series.SERVER_ERROR;
}
/**
* 處理錯(cuò)誤
*/
public void handleError(ClientHttpResponse response) throws IOException {
//獲取狀態(tài)碼
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 {
//狀態(tài)碼不為空,則處理錯(cuò)誤(主要是4xx和5xx錯(cuò)誤)
this.handleError(response, statusCode);
}
}
/**
* 處理錯(cuò)誤
*/
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客戶端錯(cuò)誤
throw HttpClientErrorException.create(statusCode, statusText, headers, body, charset);
case SERVER_ERROR:
//http服務(wù)端錯(cuò)誤
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
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對(duì)象列表
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);
}
}
//轉(zhuǎn)換抽取為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
//在TestRestTemplate類中
private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {
private NoOpResponseErrorHandler() {
}
//不做錯(cuò)誤處理
public void handleError(ClientHttpResponse response) throws IOException {
}
}
RestTemplate異常處理器被觸發(fā)源碼
- 初始化errorHandler變量
- 執(zhí)行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;
Object var14;
try {
ClientHttpRequest request = this.createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
//處理響應(yīng)
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;
}
- 處理響應(yīng),調(diào)用handleResponse()方法
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);
}
}
- 獲取異常處理器,調(diào)用getErrorHandler()方法
public ResponseErrorHandler getErrorHandler() {
//返回的就是RestTemplate中的成員變量errorHandler
return this.errorHandler;
}
RestTemplate異常處理器實(shí)踐模板
定義一個(gè)自定義的errorHandler實(shí)現(xiàn)ResponseErrorHandler接口
- errorHandler
/**
* 繼承默認(rèn)錯(cuò)誤處理器DefaultResponseErrorHandler,無需關(guān)注hasError和handlerError方法
*/
@Component
public class MyResponseErrorHandler implements ResponseErrorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);
/**
* my service進(jìn)行定制化處理
*/
@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進(jìn)行單獨(dú)處理
if (HttpStatus.UNAUTHORIZED.value() == response.getStatusCode().value()) {
myService.doSomething(url.getHost());
} else {
//繼續(xù)拋異常
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工具類
@Slf4j
public class HttpClientUtils {
//http協(xié)議
private static final String HTTP_PROTOCOL = "http";
//https協(xié)議
private static final String HTTPS_PROTOCAL = "https";
//最大連接數(shù)
private static final int MAX_CONNECT = 300;
//默認(rèn)連接數(shù)
private static final int DEFAULT_CONNECT = 200;
public HttpClientUtils(){
}
/**
* new一個(gè)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校驗(yàn)
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協(xié)議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);
//構(gòu)建CloseableHttpClient
CloseableHttpClient closeableHttpClient = build.build();
return closeableHttpClient;
} catch (Exception e ) {
log.error("異常:{}", e.getLocalizedMessage());
}
return null;
}
}
- restTemplate調(diào)用
/**
* 獲取遠(yuǎn)程連接template
*
* @return
*/
public static RestTemplate getRestTempte() {
try {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(HttpClientUtils.newHttpClientForHttpsOrHttp());
factory.setConnectTimeout(30000);
//設(shè)置handler
return new RestTemplate(factory).setErrorHandler(new MyResponseErrorHandler());
} catch (Exception e) {
return null;
}
}
定義一個(gè)自定義的errorHandler繼承DefaultResponseErrorHandler類
/**
* 繼承默認(rèn)錯(cuò)誤處理器DefaultResponseErrorHandler,無需關(guān)注hasError和handlerError方法
*/
@Component
public class MyResponseErrorHandler extends DefaultResponseErrorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);
/**
* my service進(jìn)行定制化處理
*/
@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進(jìn)行單獨(dú)處理
if (HttpStatus.UNAUTHORIZED.value() == response.getStatusCode().value()) {
myService.doSomething(url.getHost());
} else {
this.handleError(response);
}
}
}
文章來源:http://www.zghlxwxcb.cn/news/detail-654719.html
到了這里,關(guān)于SpringBoot | RestTemplate異常處理器ErrorHandler使用詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!