SpringBoot | RestTemplate异常处理器ErrorHandler使用详解

关注wx:CodingTechWork

引言

  在代码开发过程中,发现很多地方通过RestTemplate调用了第三方接口,而第三方接口需要根据某些状态码或者异常进行重试调用,此时,要么在每个调用的地方进行异常捕获,然后重试;要么在封装的RestTemplate工具类中进行统一异常捕获和封装。当然,本文不走寻常路,将会通过RestTemplate的异常处理器进行操作。

RestTemplate异常处理器介绍

分类

异常处理器功能描述
ResponseErrorHandler异常处理器接口,是restTemplate所有异常处理器的实现接口
DefaultResponseErrorHandler默认的异常处理器,处理客户端和服务端异常
ExtractingResponseErrorHandler将HTTP错误响应转换RestClientException
NoOpResponseErrorHandler不处理异常

RestTemplate异常处理器源码

ResponseErrorHandler

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

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 */@Deprecatedprotected 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];}}@Nullableprotected 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对象列表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

	//在TestRestTemplate类中private static class NoOpResponseErrorHandler extends DefaultResponseErrorHandler {private NoOpResponseErrorHandler() {}//不做错误处理public void handleError(ClientHttpResponse response) throws IOException {}}

RestTemplate异常处理器被触发源码

  1. 初始化errorHandler变量
    在这里插入图片描述
  2. 执行doExecute()方法
    @Nullableprotected <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;}
  1. 处理响应,调用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);}}
  1. 获取异常处理器,调用getErrorHandler()方法
    public ResponseErrorHandler getErrorHandler() {//返回的就是RestTemplate中的成员变量errorHandlerreturn this.errorHandler;}

RestTemplate异常处理器实践模板

定义一个自定义的errorHandler实现ResponseErrorHandler接口

  1. errorHandler
/*** 继承默认错误处理器DefaultResponseErrorHandler,无需关注hasError和handlerError方法*/
@Component
public class MyResponseErrorHandler implements ResponseErrorHandler {private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);/*** my service进行定制化处理*/@Autowiredprivate MyService myService;@Overridepublic boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {return clientHttpResponse.getStatusCode().value() != 200 && clientHttpResponse.getStatusCode().value() != 201 && clientHttpResponse.getStatusCode().value() !=302;}@Overridepublic void handleError(ClientHttpResponse clientHttpResponse) throws IOException {//遇到401进行单独处理if (HttpStatus.UNAUTHORIZED.value() == response.getStatusCode().value()) {myService.doSomething(url.getHost());} else {//继续抛异常throw new RuntimeException(clientHttpResponse.getStatusText());}}@Overridepublic 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("========================================================");}
}
  1. HttpClientUtils工具类
@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协议registerRegistry<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);//构建CloseableHttpClientCloseableHttpClient closeableHttpClient = build.build();return closeableHttpClient;} catch (Exception e ) {log.error("异常:{}", e.getLocalizedMessage());}return null;}}
  1. restTemplate调用
    /*** 获取远程连接template** @return*/public static RestTemplate getRestTempte() {try {HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(HttpClientUtils.newHttpClientForHttpsOrHttp());factory.setConnectTimeout(30000);//设置handlerreturn new RestTemplate(factory).setErrorHandler(new MyResponseErrorHandler());} catch (Exception e) {return null;}}

定义一个自定义的errorHandler继承DefaultResponseErrorHandler类

/*** 继承默认错误处理器DefaultResponseErrorHandler,无需关注hasError和handlerError方法*/
@Component
public class MyResponseErrorHandler extends DefaultResponseErrorHandler {private static final Logger LOGGER = LoggerFactory.getLogger(MyResponseErrorHandler.class);/*** my service进行定制化处理*/@Autowiredprivate MyService myService;@Overridepublic 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);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/94625.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手撕单链表

目录 链表的概念和结构 单链表的实现 申请新结点 打印 尾插 头插 尾删 头删 ​编辑 查找 在pos位置前插入元素 在pos位置后插入元素 删除pos位置的元素 删除pos位置之后的位置的元素​编辑 完整代码 SListNode.h SListNode.c 链表的概念和结构 链表是一种物理存储…

Python“牵手”1688商品评论数据采集方法,1688API申请指南

1688平台API接口是为开发电商类应用程序而设计的一套完整的、跨浏览器、跨平台的接口规范&#xff0c;1688API接口是指通过编程的方式&#xff0c;让开发者能够通过HTTP协议直接访问1688平台的数据&#xff0c;包括商品信息、店铺信息、物流信息等&#xff0c;从而实现1688平台…

【网络】传输层——TCP(滑动窗口流量控制拥塞控制延迟应答捎带应答)

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 上篇文章对TCP可靠性机制讲解了一部分&#xff0c;这篇文章接着继续讲解。 &#x1f3a8;滑动窗口 在…

CentOS 7 安装MySQL8.0.33

一、查看 CentOS 版本 要查看当前 CentOS 版本&#xff0c;你可以执行以下命令&#xff1a; cat /etc/centos-release 该命令将显示当前 CentOS 的版本信息&#xff0c;例如&#xff1a; CentOS Linux release 7.9.2009 (Core) 在这个示例中&#xff0c;CentOS 版本为 7.…

七麦analysis参数加密分析

文章目录 1. 接口分析2. 断点调式3. 补环境重写加密4. 验证结果 1. 接口分析 目标站点&#xff1a; aHR0cHM6Ly93d3cucWltYWkuY24vcmFuay9tYXJrZXRSYW5r 先刷新网页&#xff0c;请求接口中的analysis参数本次分析的目标 2. 断点调式 打上XHR断点&#xff0c;刷新页面之后断点…

Linux系统中lib64文件夹下包含:动态链接库,静态链接库,内核模块等

lib64 目录对系统稳定运行有重要作用。 目录 lib64文件动态链接库静态链接库内核模块 lib64文件 lib64 文件夹存放主要是可被程序直接加载并使用的 64 位代码模块,包括动态库、静态库、内核模块等,这些文件对程序运行都至关重要。 在 Linux 系统中,lib64 文件夹通常用于存放…

jmeter errstr :“unsupported field type for multipart.FileHeader“

在使用jmeter测试接口的时候&#xff0c;提示errstr :"unsupported field type for multipart.FileHeader"如图所示 这是因为我们 在HTTP信息头管理加content-type参数有问题 直接在HTTP请求中&#xff0c;勾选&#xff1a; use multipart/form-data for POST【中文…

性能分析之MySQL慢查询日志分析(慢查询日志)

一、背景 MySQL的慢查询日志是MySQL提供的一种日志记录,他用来记录在MySQL中响应的时间超过阈值的语句,具体指运行时间超过long_query_time(默认是10秒)值的SQL,会被记录到慢查询日志中。 慢查询日志一般用于性能分析时开启,收集慢SQL然后通过explain进行全面分析,一…

Selenium 测试用例编写

编写Selenium测试用例就是模拟用户在浏览器上的一系列操作&#xff0c;通过脚本来完成自动化测试。 编写测试用例的优势&#xff1a; 开源&#xff0c;免费。 支持多种浏览器 IE&#xff0c;Firefox&#xff0c;Chrome&#xff0c;Safari。 支持多平台 Windows&#xff0c;Li…

MySQL环境安装

文章目录 MySQL环境安装1. 卸载1.1 卸载不要的环境1.2 检查卸载系统安装包 2. 安装2.1 获取mysql官方yum源2.2 安装mysql的yum源2.3 安装mysql服务 3. 登录(1)(2)(3) 4. 配置my.cnf MySQL环境安装 说明&#xff1a; 安装与卸载中&#xff0c;用户全部切换成为root&#xff0c…

==和equals方法之间的区别,hashcode的理解

和equals方法之间的区别 hashcode是什么&#xff1f;有什么作用&#xff1f; Java中Object有一个方法&#xff1a; public native int hashcode(); &#xff08;1&#xff09;hashcode()方法的作用 hashcode()方法主要配合基于散列的集合一起使用&#xff0c;比如HashSet、…

Java数字化智慧工地管理云平台源码(人工智能、物联网、大数据)

智慧工地优势&#xff1a;"智慧工地”将施工企业现场视频管理、建筑起重机械安全监控、现场从业人员管理、物料管理、进度管理、扬尘噪声监测等现场设备有机、高效、科学、规范的结合起来真正实现工程项目业务流与现场各类监控源数据流的有效结合与深度配合&#xff0c;实…

C语言暑假刷题冲刺篇——day3

目录 一、选择题 二、编程题 &#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;C语言每日一练✨其他专栏&#xff1a;代码小游戏C语言初阶&#x1f91d;希望作者的文章能对你有…

iOS自定义下拉刷新控件

自定义下拉刷新控件 概述 用了很多的别人的下拉刷新控件&#xff0c;想写一个玩玩&#xff0c;自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些&#xff0c;毕竟谁不喜欢各种动画恰到好处的应用呢。 使用方式如下&#xff1a; tableview.refreshControl XRef…

定量分析计算51单片机复位电路工作原理 怎么计算单片机复位电容和电阻大小

下面画出等效电路图 可以知道单片机内必然有一个电阻RX&#xff0c;为了简化分析&#xff0c;我们假设他是线性电阻&#xff08;不带电容&#xff0c;电感的支路&#xff09; 还有一个基础知识&#xff1a; 电容器的充电放电曲线&#xff1a; 还需要知道电容电压的变化是连续…

Data Abstract for .NET and Delphi Crack

Data Abstract for .NET and Delphi Crack .NET和Delphi的数据摘要是一套或RAD工具&#xff0c;用于在.NET、Delphi和Mono中编写多层解决方案。NET和Delphi的数据摘要是一个套件&#xff0c;包括RemObjects.NET和Delphi版本的数据摘要。RemObjects Data Abstract允许您创建访问…

【C++精华铺】7.C++内存管理

目录 1. C语言动态内存管理 2. C内存管理方式 2.1 new/delete和new T[]/delete[] 2.1.1 操作内置类型 2.1.2 操作自定义类型 2.2 new/delete和new T[]/delete[]的原理 2.2.1 原理 2.2.2 operator new和operator delete 2.2.3 new T[]的特殊处理&#xff08;可以…

docker的资源控制管理——Cgroups

目录 一、对CPU使用率的控制 1.1 CPU 资源控制 1.2 cgroups有四大功能 1.3 设置cpu使用率上限 查看周期限制和cpu配额限制 进行cpu压力测试然后修改每个周期的使用cpu的时间&#xff0c;查看cpu使用率 1.4 设置cpu资源占用比&#xff08;设置多个容器时才有效&#xf…

chatserver服务器开发笔记

chatserver服务器开发笔记 1 chatserver2 开发环境3 编译 1 chatserver 集群聊天服务器和客户端代码&#xff0c;基于muduo、redis、mysql实现。 学习于https://fixbug.ke.qq.com/ 本人已经挂github&#xff1a;https://github.com/ZixinChen-S/chatserver/tree/main 需要该项…

数学建模之“灰色预测”模型

灰色系统分析法在建模中的应用 1、CUMCM2003A SARS的传播问题 2、CUMCM2005A长江水质的评价和预测CUMCM2006A出版社的资源配置 3、CUMCM2006B艾滋病疗法的评价及疗效的预测问题 4、CUMCM2007A 中国人口增长预测 灰色系统的应用范畴大致分为以下几方面: (1&#xff09;灰色关…