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] 问题的更多相关文章

  1. 【FAQ】Could not extract response: no suitable HttpMessageConverter found for respo

    原因: 1:某些必须传入的参数没传 2:返回对象的接收类型不一致

  2. no suitable HttpMessageConverter found for response type

    在使用 RestTemplate 或者 Spring Cloud 的时候,经常会出现这个错误. 基本上出现的场景都是,我们要json,结果来了个 text/html. 两个原因: 第一个就是:服务器返 ...

  3. RestTemplate HttpMessageConverter报错的解决方案no suitable HttpMessageConverter

    错误 no suitable HttpMessageConverter found for response type and content type [text/html;charset=UTF- ...

  4. RestTemplate 微信接口 text/plain HttpMessageConverter

    一.背景介绍 使用 Spring Boot 写项目,需要用到微信接口获取用户信息. 在 Jessey 和 Spring RestTemplate 两个 Rest 客户端中,想到尽量不引入更多的东西,然 ...

  5. no suitable HttpMessageConverter found for request type [java.lang.Integer]

    今天在使用Spring Template的时候遇到了这个异常: no suitable HttpMessageConverter found for request type [java.lang.I ...

  6. spring mvc Response header content type

    Xml代码 <bean class ="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAd ...

  7. response小结(五)—通过response实现请求重定向

    请求重定向指的是一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向.302状态码和location头即可实现重定向. 请求重定向最常见的应用场景就是用户登录. 下面 ...

  8. response小结(一)——用response向客户端输出中文数据(乱码问题分析)

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象,和代表响应的response对象.request和response对象既然代表请求和响应,那我们要 ...

  9. response的contentType的类型值Response.ContentType

    MIME类型的含义 MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开.多用于指定一些客户端自定义的文件名,以及一些媒体 ...

  10. 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 ...

随机推荐

  1. 使用require.context实现优雅的预加载

    前言 在前端开发中,对页面花里胡哨度[注1]要求越高的页面,用到的图片.音频什么的就越多,比如什么结婚请柬.展会请柬.发布会宣传页.数据大屏.虽然现在浏览器不允许网页在没有用户交互的情况下播放音频,但 ...

  2. stl------stack与queue

    stack与queue 一.stack 二.queue 例题:详见我的另一篇博文: 栈------表达式求值 http://www.cnblogs.com/Cloud-king/p/8453703.h ...

  3. Vue2到Vue3的改变

    一.Vue2->Vue3 如果有Vue2的基础,并在此基础上学习Vue3,并不需要把完整的官网看完,我们只需要关注一下新功能和非兼容的变化即可进行开发. 二.Vue3变化 统一元素上使用的v-i ...

  4. 2022-12-19:大的国家。如果一个国家满足下述两个条件之一,则认为该国是 大国 : 面积至少为 300 万平方公里(即,3000000 km2),或者 人口至少为 2500 万(即 250000

    2022-12-19:大的国家.如果一个国家满足下述两个条件之一,则认为该国是 大国 : 面积至少为 300 万平方公里(即,3000000 km2),或者 人口至少为 2500 万(即 250000 ...

  5. 2022-07-10:以下go语言代码输出什么?A:A,B;B:A,C:A,fatal error;D:fatal error... func main() { var m sync.Mute

    2022-07-10:以下go语言代码输出什么?A:A,B:B:A,C:A,fatal error:D:fatal error- func main() { var m sync.Mutex fmt. ...

  6. 2022-04-15:给定一个非负数组arr,学生依次坐在0~N-1位置,每个值表示学生的安静值, 如果在i位置安置插班生,那么i位置的安静值变成0,同时任何同学都会被影响到而减少安静值, 同学安静值

    2022-04-15:给定一个非负数组arr,学生依次坐在0~N-1位置,每个值表示学生的安静值, 如果在i位置安置插班生,那么i位置的安静值变成0,同时任何同学都会被影响到而减少安静值, 同学安静值 ...

  7. 2021-04-22:给定很多线段,每个线段都有两个数[start, end],表示线段开始位置和结束位置,左右都是闭区间,规定:1)线段的开始和结束位置一定都是整数值,2)线段重合区域的长度必须>=

    2021-04-22:给定很多线段,每个线段都有两个数[start, end],表示线段开始位置和结束位置,左右都是闭区间,规定:1)线段的开始和结束位置一定都是整数值,2)线段重合区域的长度必须&g ...

  8. Django4全栈进阶之路20 项目实战(三种方式开发部门管理):方式一:FBV

    1.模型 from django.db import models from django.contrib.auth.models import User # Create your models h ...

  9. jdg安装及环境设置

    1.下载java8 java11 到默认路径 2.右击我的电脑,点击属性 3.点击高级系统设置 4.点击环境变量 5.点击新建 6. 按照提示新建条目 第一个 变量名填入: JAVA_HOME 变量值 ...

  10. odoo开发教程八:qweb引擎

    一:简介 QWeb是一个基于xml的模板引擎,用于生成HTML片段和页面,模板指令是写在xml标签中的以t-开头的属性,比如t-if如果要让一个标签不被渲染,可以采用t来包裹,这样会执行它里面的命令但 ...