之前项目都是使用 HttpClient 进行 api 调用,代码较为复杂。新项目正好想试试 spring 自家封装的 RestTemplate,没想到第一个小程序服务端获取 openid 的请求直接就翻车了,异常如下:
org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [class ***] and content type [text/plain]
debug 发现响应内容正常,出错的原因是微信这个坑货 响应头中的 Content-Type 是 text/plain。
解决方法有两种,如下:
使用 String 类型接收
String res = restTemplate.getForObject(url,String.class,map);
// 使用json转换工具将String类型的数据转换为对象
这个方法简单有效,但是每个 api 调用的地方都这么写下感觉挺丑的
自定义 HttpMessageConverter
研究源码发现 RestTemplate 在静态代码块和无参构造中通过类加载器判断,初始化对应的消息转换器
// RestTemplate部分源码
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
this.messageConverters.add(new JsonbHttpMessageConverter());
}
//...
}
我的项目中使用 jackson 作为 json 序列化工具,所以直接继承 MappingJackson2HttpMessageConverter 这个实现就好了。如果你使用其他的 json 序列化工具,就根据源码自行修改
// CustomJacksonHttpMessageConverter.java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.util.Collections;
public class CustomJacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {
public CustomJacksonHttpMessageConverter() {
this(Jackson2ObjectMapperBuilder.json().build());
}
public CustomJacksonHttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper);
// 这里是重点,增加支持的类型,看你的情况加
// 我这里目前只需要加个 TEXT/PLAIN
setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_PLAIN,
MediaType.APPLICATION_JSON,
new MediaType("application", "*+json")));
}
}
最后修改下 RestTemplate 的配置,将自定义的消息转换器和原来的替换
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
// 创建 RestTemplate 实例, 我这里使用的OkHttp
RestTemplate restTemplate = new RestTemplate(new OkHttp3ClientHttpRequestFactory());
// 替换默认的 Jackson 消息转换器实现
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
Iterator<HttpMessageConverter<?>> iterator = messageConverters.iterator();
while (iterator.hasNext()) {
HttpMessageConverter<?> next = iterator.next();
if (next instanceof MappingJackson2HttpMessageConverter) {
iterator.remove();
break;
}
}
messageConverters.add(new CustomJacksonHttpMessageConverter());
return restTemplate;
}
}