Could not extract response: no suitable `HttpMessageConverter` found for response type [class wechat.xx] and content type [text/plain] 问题
1. 问题复现
话不多说,先贴出问题代码:这里的GetUserInfoByAccessToken
是我自定义的一个实体类。
GetUserInfoByAccessToken getUserInfoByAccessTokenString = restTemplate.getForObject(userInfoByAccessCodeURL, GetUserInfoByAccessToken.class);
异常信息:Could not extract response: no suitable HttpMessageConverter
found for response type [class wechat.wxRes.GetUserInfoByAccessToken] and content type [text/plain],很明显这段异常的意思是在指定返回类型为GetUserInfoByAccessToken,并且服务端响应报文的content-type为text/plain的情况下找不到一个合适的HttpMessageConverter
来处理这种情况
2. 处理方法
这里举例两种处理请求
1.首先StringHttpMessageConverter
这个处理器是可以处理content-type为text/plain的响应报文的。但阅读源码知道必须放回类型是String才可以使用它,所有我们需要改写下代码,将放回类型改为String。需要的时候可以利用JSON
工具类将其转为你需要的类型。
GetUserInfoByAccessToken getUserInfoByAccessTokenString = restTemplate.getForObject(userInfoByAccessCodeURL, String.class);
需要注意的是使用StringHttpMessageConverter
容易出现中文乱码的情况,因为它默认支持的字符集是ISO-8859-1
,这种时候可以参考以下代码更改StringHttpMessageConverter
的默认字符集,我这里将其改为utf-8
了。
RestTemplate customRestTemplate = new RestTemplate();
List<HttpMessageConverter<?>> list = customRestTemplate.getMessageConverters();
for (HttpMessageConverter<?> httpMessageConverter : list) {
if(httpMessageConverter instanceof StringHttpMessageConverter) {
((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(Charset.forName("utf-8"));
break;
}
}
2.往restTemplate
的转换器里再加一个支持JSON
转换的转换器,比如MappingJackson2HttpMessageConverter
。
RestTemplate customRestTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML,MediaType.TEXT_PLAIN));
restTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
GetUserInfoByAccessToken getUserInfoByAccessTokenString = restTemplate.getForObject(userInfoByAccessCodeURL, GetUserInfoByAccessToken.class);
3. 源码分析问题
3.1 关键代码extractData
方法
extractData
方法将接口请求拿到的响应报文拿来给HttpMessageConverter
解析,这里会找到合适的解析器来解析响应报文,解析成我们指定的返回类型的数据,如果找不到或者处理出现异常就会抛出异常。
代码清单1-1 org.springframework.web.client.HttpMessageConverterExtractor#extractData
// 这里的入参是请求之后的响应体
public T extractData(ClientHttpResponse response) throws IOException {
//创建一个名为responseWrapper的MessageBodyClientHttpResponseWrapper,用于包装响应对象response,方便操作响应数据。
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
// 检查响应是否有消息体,并且消息体不为空。如果不满足条件,则返回null。
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
// 获取响应内容类型contentType。
MediaType contentType = getContentType(responseWrapper);
try {
// 遍历已注册的HttpMessageConverter列表。
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
// 对于实现了GenericHttpMessageConverter接口的转换器,检查是否可以读取responseType对应的类型,并且内容类型匹配。
if (messageConverter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericMessageConverter =
(GenericHttpMessageConverter<?>) messageConverter;
if (genericMessageConverter.canRead(this.responseType, null, contentType)) {
if (logger.isDebugEnabled()) {
ResolvableType resolvableType = ResolvableType.forType(this.responseType);
logger.debug("Reading to [" + resolvableType + "]");
}
return (T) genericMessageConverter.read(this.responseType, null, responseWrapper);
}
}
// 如果没有找到合适的GenericHttpMessageConverter,则检查是否指定了responseClass。
if (this.responseClass != null) {
// 如果指定了responseClass,则检查是否有转换器可以读取该类型,并且内容类型匹配。见相关代码`canRead`方法中的代码清单1-2
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
String className = this.responseClass.getName();
logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
}
// 如果匹配成功,使用该转换器读取响应数据,并返回结果。
return (T) messageConverter.read((Class) this.responseClass, responseWrapper);
}
}
}
}
catch (IOException | HttpMessageNotReadableException ex) {
throw new RestClientException("Error while extracting response for type [" +
this.responseType + "] and content type [" + contentType + "]", ex);
}
throw new UnknownContentTypeException(this.responseType, contentType,
response.getRawStatusCode(), response.getStatusText(), response.getHeaders(),
getResponseBody(response));
}
3.2 相关代码messageConverter.canRead(this.responseClass, contentType)
方法
canRead(java.lang.Class, org.springframework.http.MediaType)
方法判断当前的HttpMessageConverter
是否可以读取响应报文ContentType
为服务端指定的数据,并且内容和你指定的返回值类型匹配。
代码清单1-2 org.springframework.http.converter.AbstractHttpMessageConverter#canRead(java.lang.Class, org.springframework.http.MediaType)
// 判断`HttpMessageConverter`转换器是否可以读取该ContentType的数据,并且内容和你指定的返回值类型匹配
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
// supports判断HttpMessageConverter转换器是否支持你指定的返回类型,参考代码清单1-3。canRead
return supports(clazz) && canRead(mediaType);
}
这是StringHttpMessageConverter
的supports方法,可以看出他可以处理返回类型为String的数据。
代码清单1-3 org.springframework.http.converter.StringHttpMessageConverter#supports
public boolean supports(Class<?> clazz) {
return String.class == clazz;
}
上面代码supports方法返回true会调用canRead(org.springframework.http.MediaType)
方法,这段代码主要就是判断当前的HttpMessageConverter
是否可以处理content-type为服务端指定类型的响应报文,比如content-type为text/plain。
代码清单1-4 org.springframework.http.converter.AbstractHttpMessageConverter#canRead(org.springframework.http.MediaType)
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
4.关键点截图
以下是我在调试中截取的一些图片。
这里可以看到响应体的contentType
为text/plain,接下来就要找可以处理这种响应类型的HttpMessageConverter
。
这里可以看到已注册的HttpMessageConverter
列表里面有九个元素,并且通过他们的supportedMediaTypes
属性看到他们可以处理的contentType
。
首先判断HttpMessageConverter
是否可以读取我们指定的返回类,这里我指定的是我自定义的一个返回类GetUserInfoByAccessToken.class
在这里是在判断当前HttpMessageConverter
是否可以处理当前响content-type为text/plain的响应报文。
Could not extract response: no suitable `HttpMessageConverter` found for response type [class wechat.xx] and content type [text/plain] 问题的更多相关文章
- 【FAQ】Could not extract response: no suitable HttpMessageConverter found for respo
原因: 1:某些必须传入的参数没传 2:返回对象的接收类型不一致
- no suitable HttpMessageConverter found for response type
在使用 RestTemplate 或者 Spring Cloud 的时候,经常会出现这个错误. 基本上出现的场景都是,我们要json,结果来了个 text/html. 两个原因: 第一个就是:服务器返 ...
- RestTemplate HttpMessageConverter报错的解决方案no suitable HttpMessageConverter
错误 no suitable HttpMessageConverter found for response type and content type [text/html;charset=UTF- ...
- RestTemplate 微信接口 text/plain HttpMessageConverter
一.背景介绍 使用 Spring Boot 写项目,需要用到微信接口获取用户信息. 在 Jessey 和 Spring RestTemplate 两个 Rest 客户端中,想到尽量不引入更多的东西,然 ...
- no suitable HttpMessageConverter found for request type [java.lang.Integer]
今天在使用Spring Template的时候遇到了这个异常: no suitable HttpMessageConverter found for request type [java.lang.I ...
- spring mvc Response header content type
Xml代码 <bean class ="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAd ...
- response小结(五)—通过response实现请求重定向
请求重定向指的是一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向.302状态码和location头即可实现重定向. 请求重定向最常见的应用场景就是用户登录. 下面 ...
- response小结(一)——用response向客户端输出中文数据(乱码问题分析)
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象,和代表响应的response对象.request和response对象既然代表请求和响应,那我们要 ...
- response的contentType的类型值Response.ContentType
MIME类型的含义 MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开.多用于指定一些客户端自定义的文件名,以及一些媒体 ...
- RestTemplate异常no suitable HttpMessageConverter found for request type [java.lang.Integer]
GET方式,参数必须放在URL后面,http://xxx/list?name={name}&age={age} package com.chelizi.xiruo.xframework.uti ...
随机推荐
- Prism Sample 20-NavigateToExistingViews
上一个例子介绍了INavigationAware中的OnNavitationTo,这次是第二个实现函数. IsNavitationTarget,这个名字有点误导,真实的作用是,当从其它页面导航至本页面 ...
- LeetCode 周赛 344(2023/05/07)手写递归函数的固定套路
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 今天下午有力扣杯战队赛,不知道官方是不是故意调低早上周赛难度给选手们练练手. 往期周赛回 ...
- 2023-03-17:使用Go语言和FFmpeg库实现音频重采样解码,并将其保存为PCM格式的文件。
2023-03-17:使用Go语言和FFmpeg库实现音频重采样解码,并将其保存为PCM格式的文件. 答案2023-03-17: 在音视频处理领域,常常需要对音频进行重采样和解码,以便于后续的处理和分 ...
- 2022-04-27:Alice 有一个下标从 0 开始的数组 arr ,由 n 个正整数组成。她会选择一个任意的 正整数 k 并按下述方式创建两个下标从 0 开始的新整数数组 lower 和 hig
2022-04-27:Alice 有一个下标从 0 开始的数组 arr ,由 n 个正整数组成.她会选择一个任意的 正整数 k 并按下述方式创建两个下标从 0 开始的新整数数组 lower 和 hig ...
- vue全家桶进阶之路40:Vue3父件传值给子件
在Vue3中,可以通过props将父组件的数据传递给子组件.具体步骤如下: 在父组件中定义要传递给子组件的数据,可以是data属性中的数据或者是计算属性computed中的数据. 在子组件中通过pro ...
- Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535.
问题描述 新建表或者修改表varchar字段长度的时候,出现这个错误 Row size too large. The maximum row size for the used table type, ...
- 泰裤辣!!!手摸手教学,如何训练一个你的专属AI歌姬~
最近在做AIGC的项目,不过是与图片相关的,现在的模型效果可比前几年图片替换效果好多了.之前尝试过用 faceswap 工具来进行人脸替换的,具体可以参看下我之前的这篇文章:https://blog. ...
- 苹果WWDC发布会总结
今年的全球开发者大会没有让人失望.在今天的主题演讲中,苹果首次展示了备受期待的混合现实耳机,证实了过去几个月出现的许多谣言. 虽然这次苹果的 Vision Pro耳机成为了焦点,但该公司还发布了一些其 ...
- .netcore中的虚拟文件EmbeddedFile
以前一直比较好奇像swagger,cap,skywalking等组件是如何实现引用一个dll即可在网页上展示界面的,难道这么多html,js,css等都是硬编码写死在代码文件中的?后面接触apb里面也 ...
- AB实验:科学归因与增长的利器
第一章 AB实验的基本原理和应用 AB实验的相关概念: 3个基本参数:实验参与单元.实验控制参数.实验指标 2个核心价值:验证因果关系.量化策略效果 2个关键特性:先验性.并行性 基本流程:分流 -& ...