解决使用RestTemplate时报错RestClientException的问题
作者:墨一般的白色 发布时间:2023-05-27 19:46:36
使用RestTemplate时报错RestClientException
这是自己封装的一个发送请求的方法
public Map<String, Object> sendRequest(Map<String, Object> body,String sessionId,String url) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new BdMappingJackson2HttpMessageConverter());
Map<String, Object> map = new HashMap<>();
try {
ParameterizedTypeReference<Map<String, Object>> typeRef = new ParameterizedTypeReference<Map<String, Object>>() {
};
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.set("cookie", "SESSION="+sessionId);
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body,headers);
ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange(url, HttpMethod.POST,
requestEntity,typeRef);
map = responseEntity.getBody();
log.info(map.toString());
} catch (HttpStatusCodeException e) {
log.error(e.getResponseBodyAsString(), e);
map = JsonUtil.toMap(e.getResponseBodyAsString());
}
return map;
}
这是自定义的一个http信息Converter
public class BdMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
public BdMappingJackson2HttpMessageConverter(){
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_HTML);
mediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
setSupportedMediaTypes(mediaTypes);
}
}
我遇到的第一个问题是这样的
Could not write request: no suitable HttpMessageConverter found for request type [java.util.HashMap] and content type [application/octet-stream]
意思大概是无法写入请求:找不到适用于请求类型[java.util.HashMap]和内容类型[application/octet stream]的HttpMessageConverter
它默认只处理application/json:JSON数据格式,这个二进制数据流格式不支持,所以我在那个自定义的Converter加上去了。
紧接着又遇到第二个问题
Could not extract response: no suitable HttpMessageConverter found for response type [java.util.Map<java.lang.String, java.lang.Object>] and content type [text/xml;charset=UTF-8]
无法提取响应:找不到适合于响应类型[java.util.Map<java.lang.String,java.lang.Object>]和内容类型[textml/charset=UTF-8]的HttpMessageConverter
这是那边回调过来的Content-Type类型是text/xml它同样解析不了,所以我把这个也加上去了,至此就ok了。
常见的Content-Type类型:
text/html
:HTML格式text/plain
:纯文本格式image/png
:png图片格式application/json
:JSON数据格式application/octet-stream
:二进制流数据application/x-www-form-urlencoded
:表单中默认的encType,表单数据被编码为key/value格式发送到服务器multipart/form-data
:需要在表单中进行文件上传时,就需要使用该格式
RestTemplate的错误处理
我们的项目属于微服务架构,两个基础的服务分别是网关和认证鉴权服务。
在前端访问后台服务的时候,都经过网关转发,转发之前会进行鉴权认证,根据鉴权结果判断是否可以进行相应的请求转发。
问题描述
认证鉴权服务中,判断Token对应的人员是否有相应的权限,如果没有权限,返回401状态码并在响应体中传回错误信息。
网关与鉴权服务之间的服务调用通过RestTemplate进行(可以考虑转为Feign做声明式的服务调用),然而如果鉴权服务返回401的情况下,网关服务直接报出HttpClientErrorException,让人一头雾水。
实际上,答案都在源码中,看一下RestTemplate的源码就知晓了。
ErrorHandler
在RestTemplate中,有一个成员变量ResponseErrorHandler。
ResponseErrorHandler是一个接口,包括两个方法:
public interface ResponseErrorHandler {
boolean hasError(ClientHttpResponse var1) throws IOException;
void handleError(ClientHttpResponse var1) throws IOException;
}
这个接口有一个默认实现DefaultResponseErrorHandler。该方法中,判断是否发生error的方法hasError最终调用的方法如下:
protected boolean hasError(HttpStatus statusCode) {
return statusCode.series() == Series.CLIENT_ERROR || statusCode.series() == Series.SERVER_ERROR;
}
很明显,根据响应状态吗为4xx或者5xx来认定发生了错误。而错误处理在handleError中:
public void handleError(ClientHttpResponse response) throws IOException {
HttpStatus statusCode = this.getHttpStatusCode(response);
switch(null.$SwitchMap$org$springframework$http$HttpStatus$Series[statusCode.series().ordinal()]) {
case 1:
throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
case 2:
throw new HttpServerErrorException(statusCode, response.getStatusText(), response.getHeaders(), this.getResponseBody(response), this.getCharset(response));
default:
throw new RestClientException("Unknown status code [" + statusCode + "]");
}
}
可见,4xx的状态吗会抛出HttpClientErrorException;5xx的状态码会抛出HttpServerErrorException。这也就是我们一开始遇到的问题的原因所在了。而在handleError中,执行了response.getBody(),这就导致我们后续获取不到响应体了,如果要获取的话,需要进行自定义相关处理。
解决办法
如果RestTemplate的应用场景比较统一,可以自定义ResponseErorHandler(派生自DefaultResponseErrorHandler)来接管错误处理,进行自己想要的处理。
而我们的网关中,对于页面跳转类的请求和Rest API类的请求,处理办法显然是不一样的。所以最终处理是catch异常,然后进行重定向的处理操作。
来源:https://blog.csdn.net/qq_31442649/article/details/105053671
猜你喜欢
- 思想利用栈和队列都可以实现树的迭代遍历。递归的写法将这个遍历的过程交给系统的堆栈去实现了,所以思想都是一样的、无非就是插入值的时机不一样。利
- mybatis-spring:@MapperScan注解在demo: springboot+mybatis的示例中,dao层接口使用了注解@
- 本文实例为大家分享了C#实现扫雷游戏的具体代码,供大家参考,具体内容如下一、实验目的:1、掌握c#窗体和控件的常用属性和功能2、完成扫雷游戏
- 本文实例讲述了C#.net实现在Winform中从internet下载文件的方法。分享给大家供大家参考。具体如下:自己做了一个通用的软件自动
- Java 中的运算符与 C 语言基本一致。1、算术运算符操作符描述例子+加法 : 相加运算符两侧的值A + B 等于 30-减法 : 左操作
- 基本概念Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制并发访问
- 前言在介绍使用微信自定义分享前,我们来先了解一下什么是自定义分享?访问自定义微信外链地址页面,点击红色框位置进行分享给朋友或者朋友圈,具体操
- Swing 的组件与AWT 组件相似,但又为每一个组件增添了新的方法,并提供了更多的高级组件.Swing 的基本组件:1.按钮(JButto
- 本文实例为大家分享了winform实现五子棋游戏的具体代码,供大家参考,具体内容如下利用数组,根据新旧数组值的不同,获取那个点是什么棋子;说
- 利用Java连接MySQL做登陆界面,供大家参考,具体内容如下1、首先需要建立一个类,在这里,我命名为newLoginnewLogin类的代
- 在很多场景下,maven不能直接访问到外网时,使用代理是其中常见的一种方式。这篇文章整理一下常见的maven中设置代理的方法。代理服务器代理
- 什么是响应式简单来说当数据发生变化时,对数据有依赖的代码会重新执行。例如在Vue中,当我们的数据发生改变,界面上对该数据的引用组件会重新渲染
- 近期很多小伙伴问我,为何启动项目的时候Spring 或 Spring MVC资源文件找不到
- 流读取导致StringBuilder.toString()乱码乱码问题StringBuilder sb = new StringBuilde
- C#调用C++ dll string类型返回为了这个问题,百度了一堆不靠谱的资料,什么C#调用c++类型对应啥的,说用string ,Str
- 简介对于一个APP来说,肯定会有一个AppBar,这个AppBar一般包含了APP的导航信息等。虽然我们可以用一个固定的组件来做为AppBa
- 目标了解HTTP 请求/响应头及常见的属性;了解如何使用SpringBoot处理头信息 ;了解如何使用SpringBoot处理Cookie
- [LeetCode] 2. Add Two Numbers 两个数字相加You are given two non-empty&n
- springboot中集成jpa需要再pom文件中添加jpa的jar包,使用springboot的话iju不用自己规定版本号了,自动管理依赖
- 有时,通过Runtime.getRuntime().exec()执行命令的有效负载有时会失败。使用Web Shell,反序列化利用或通过其他