转自:https://ethendev.github.io/2018/11/06/RestTemplate-error-handler/

一些 API 的报错信息通过 Response 的 body返回。使用 HttpClient 能正常获取到 StatusCode 和 body 中的错误提示。然而使用 RestTemplate ,会直接抛出下面的异常。如果想获取原始的信息并进一步处理会比较麻烦。

org.springframework.web.client.HttpClientErrorException: 404 null
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:94)
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:79)
at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:777)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:730)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:704)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:621)

RestTemplate 异常处理流程

下面看一下原因, RestTemplate 中的 getForObject, getForEntity 和 exchange 等常用方法最终都是调用 doExecute 方法。下面是 doExecute 方法源码

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {

    private ResponseErrorHandler errorHandler;
...... @Nullable
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null; String resource;
try {
ClientHttpRequest request = this.createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
} response = request.execute();
// 处理 Response
this.handleResponse(url, method, response);
if (responseExtractor != null) {
Object var14 = responseExtractor.extractData(response);
return var14;
} resource = null;
} catch (IOException var12) {
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 resource;
} 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 {
this.logger.debug(method.name() + " request for \"" + url + "\" resulted in " + response.getRawStatusCode() + " (" + response.getStatusText() + ")" + (hasError ? "; invoking error handler" : ""));
} catch (IOException var7) {
;
}
}
// 异常处理
if (hasError) {
errorHandler.handleError(url, method, response);
} }
}

从下面的代码可以看出,DefaultResponseErrorHandler 捕获并抛出了异常。

public class DefaultResponseErrorHandler implements ResponseErrorHandler {
... protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
switch(statusCode.series()) {
case CLIENT_ERROR:
throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
case SERVER_ERROR:
throw new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
default:
throw new UnknownHttpStatusCodeException(statusCode.value(), response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
}
}
}

如果想自己捕获异常信息,自己处理异常的话可以通过实现 ResponseErrorHandler 类来实现。其源码如下:

public interface ResponseErrorHandler {

    // 标示 Response 是否存在任何错误。实现类通常会检查 Response 的 HttpStatus。
boolean hasError(ClientHttpResponse var1) throws IOException; // 处理 Response 中的错误, 当 HasError 返回 true 时才调用此方法。
void handleError(ClientHttpResponse var1) throws IOException; // handleError 的替代方案,提供访问请求URL和HTTP方法的额外信息。
default void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
this.handleError(response);
}
}

自定义 RestTemplate 异常处理

如果想像 HttpClient 一样直接从 Response 获取 HttpStatus 和 body 中的报错信息 而不抛出异常,可以通过下面的代码实现:

public class CustomErrorHandler implements ResponseErrorHandler {

    @Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return true;
} @Override
public void handleError(ClientHttpResponse response) throws IOException { }
}

设置 RestTemplate 的异常处理类

restTemplate.setErrorHandler(new CustomErrorHandler());
ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, null, String.class);
System.out.println(response.getBody());

输出结果

{"code":404,"result":null,"message":"Resources not found"} 

自定义 RestTemplate 异常处理 (转)的更多相关文章

  1. mvc自定义全局异常处理

    异常信息处理是任何网站必不可少的一个环节,怎么有效显示,记录,传递异常信息又成为重中之重的问题.本篇将基于上篇介绍的html2cancas截图功能,实现mvc自定义全局异常处理.先看一下最终实现效果: ...

  2. ASP.NET Core 中间件自定义全局异常处理

    目录 背景 ASP.NET Core过滤器(Filter) ASP.NET Core 中间件(Middleware) 自定义全局异常处理 .Net Core中使用ExceptionFilter .Ne ...

  3. SpringBoot系列——自定义统一异常处理

    前言 springboot内置的/error错误页面并不一定适用我们的项目,这时候就需要进行自定义统一异常处理,本文记录springboot进行自定义统一异常处理. 1.使用@ControllerAd ...

  4. Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志

    在使用SimpleMappingExceptionResolver实现统一异常处理后(参考Spring MVC的异常统一处理方法), 发现出现异常时,log4j无法在控制台输出错误日志.因此需要自定义 ...

  5. Laravel实践-自定义全局异常处理

    在做API时,需要对一些异常进行全局处理 百牛信息技术bainiu.ltd整理发布于博客园比如添加用户执行失败时,需要返回错误信息 // 添加用户 $result = User::add($user) ...

  6. 精讲RestTemplate第7篇-自定义请求失败异常处理

    本文是精讲RestTemplate第7篇,前篇的blog访问地址如下: 精讲RestTemplate第1篇-在Spring或非Spring环境下如何使用 精讲RestTemplate第2篇-多种底层H ...

  7. ASP.NET MVC自定义异常处理

    1.自定义异常处理过滤器类文件 新建MyExceptionAttribute.cs异常处理类文件

  8. asp.net core 自定义异常处理中间件

    asp.net core 自定义异常处理中间件 Intro 在 asp.net core 中全局异常处理,有时候可能不能满足我们的需要,可能就需要自己自定义一个中间件处理了,最近遇到一个问题,有一些异 ...

  9. SpringCloud微服务实战——搭建企业级开发框架(七):自定义通用响应消息及统一异常处理

      平时开发过程中,无可避免我们需要处理各类异常,所以这里我们在公共模块中自定义统一异常,Spring Boot 提供 @RestControllerAdvice 注解统一异常处理,我们在GitEgg ...

随机推荐

  1. <数据结构>XDOJ324,325图的优先遍历

    XDOJ324.图的广度优先遍历 问题与解答 问题描述 已知无向图的邻接矩阵,以该矩阵为基础,给出广度优先搜索遍历序列,并且给出该无向图的连通分量的个数.在遍历时,当有多个点可选时,优先选择编号小的顶 ...

  2. SpringBoot集成Log4j2框架

    1.说明 本文详细介绍Spring Boot集成Log4j2框架的方法, 基于已经创建好的Spring Boot工程, 由于Spring Boot默认使用的是Logback框架, 需要先排除掉Logb ...

  3. Laravel Redis分布式锁实现源码分析

    首先是锁的抽象类,定义了继承的类必须实现加锁.释放锁.返回锁拥有者的方法. namespace Illuminate\Cache; abstract class Lock implements Loc ...

  4. OpenIddict 登录及详细流程解析

    GitHub上实例都是集成了Identity来实现,我这里去掉了相关东西,实现自定义的登录满足自己的结构要求 服务端配置添加数据库服务以及定时任务服务 builder.Services.AddDbCo ...

  5. JDBC 处理sql查询多个不确定参数

    JDBC程序,为了防止SQL注入,通常需要进行参数化查询,但是如果存在多个不确定参数,就比较麻烦了,查阅了一些资料,最后解决了这个问题,现在这里记录一下:   public List<TabDl ...

  6. 虚拟机上CentOS7网络配置

    如果图片损坏:点击链接:https://www.toutiao.com/i6493449418249863693/ 设置网络 首先打开虚拟网络编辑器 权限打开 选择NAT模式,设置IP 应用确定之后, ...

  7. springboot打包第三方jar包是失败

    在项目开发时有时我们需要引入一些在maven仓库中不存在的包 一.配置maven环境变量 在path环境变量中添加 %maven_home%\bin (window10环境下) cmd界面输入 mvn ...

  8. Flowable实战(六)集成JPA

      上文提到,Flowable所有的表单数据都保存在一张表(act_hi_varinst)中,随着时间的推移,表中数据越来越多,再加上数据没有结构优化,查询使用效率会越来越低.   在Flowable ...

  9. [FatFs 学习] SD卡总结-SPI模式

    SD卡为移动设备提供了安全的,大容量存储解决方法.它本身可以通过两种总线模式和MCU进行数据传输,一种是称为SD BUS的4位串行数据模式,另一种就是大家熟知的4线SPI Bus模式.一些廉价,低端的 ...

  10. 【记录一个问题】cv::cuda::BufferPool发生assert错误

    cv::cuda::setBufferPoolUsage(true); const int width = 512; const int height = 848; const int channel ...