解决使用RestTemplate时报错RestClientException的问题
目录
- 使用RestTemplate时报错RestClientException
- 这是自己封装的一个发送请求的方法
- 这是自定义的一个http信息Converter
- RestTemplate的错误处理
- 问题描述
- ErrorHandler
- 解决办法
使用RestTemplate时报错RestClientException
这是自己封装的一个发送请求的方法
publicMap sendRequest(Map body,String sessionId,String url) {RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(new BdMappingJackson2HttpMessageConverter()); Map map = new HashMap<>(); try {ParameterizedTypeReference
这是自定义的一个http信息Converter
public class BdMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {public BdMappingJackson2HttpMessageConverter(){ListmediaTypes = 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.util.Map] and content type [text/xml; charset=UTF-8]
这是那边回调过来的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异常,然后进行重定向的处理操作。
【解决使用RestTemplate时报错RestClientException的问题】以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
推荐阅读
- parallels|parallels desktop 解决网络初始化失败问题
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- 由浅入深理解AOP
- 【译】20个更有效地使用谷歌搜索的技巧
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus|MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决
- MybatisPlus使用queryWrapper如何实现复杂查询
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- iOS中的Block