第一次玩微信小程序,调用第一个接口jscode2session居然就出问题了。采用RestTemplate访问https://api.weixin.qq.com/sns/jscode2session,就提示异常:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.solar.app.model.weixin.WxBaseUserInfo] and content type [text/plain]
查询度娘,原因如下:
虽然这里的微信接口返回的是 Json 格式的数据,但是在返回的 Header 里面的 Content-Type 值却是 text/plain,而RestTemplate的消息转换器集合中默认是没有这个类型的,所以需要把这个类型加入到消息转换器中。
分析:
public RestTemplate() { this.messageConverters = new ArrayList(); this.errorHandler = new DefaultResponseErrorHandler(); this.headersExtractor = new RestTemplate.HeadersExtractor(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(new StringHttpMessageConverter()); this.messageConverters.add(new ResourceHttpMessageConverter(false)); if (!shouldIgnoreXml) { try { this.messageConverters.add(new SourceHttpMessageConverter()); } catch (Error var2) { } } this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); if (romePresent) { this.messageConverters.add(new AtomFeedHttpMessageConverter()); this.messageConverters.add(new RssChannelHttpMessageConverter()); } if (!shouldIgnoreXml) { if (jackson2XmlPresent) { this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); } else if (jaxb2Present) { this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } } if (jackson2Present) { this.messageConverters.add(new MappingJackson2HttpMessageConverter()); } else if (gsonPresent) { this.messageConverters.add(new GsonHttpMessageConverter()); } else if (jsonbPresent) { this.messageConverters.add(new JsonbHttpMessageConverter()); } else if (kotlinSerializationJsonPresent) { this.messageConverters.add(new KotlinSerializationJsonHttpMessageConverter()); } if (jackson2SmilePresent) { this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter()); } if (jackson2CborPresent) { this.messageConverters.add(new MappingJackson2CborHttpMessageConverter()); } this.uriTemplateHandler = initUriTemplateHandler(); }
而springboot内置的就是jackson来进行json的序列化和反序列号,通过debug也可以验证这一点(jackson2Present == true),也就是说RestTemplate会默认加入MappingJackson2HttpMessageConverter这个转换器,它有两个构造方法,我们可以发现其支持的MediaType为application/json
public MappingJackson2HttpMessageConverter() { this(Jackson2ObjectMapperBuilder.json().build()); // 这里调用的是第二个构造方法 } public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) { super(objectMapper, new MediaType[]{MediaType.APPLICATION_JSON, new MediaType("application", "*+json")}); }
通过debug,发现RestTemplate会执行doExecute方法
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "URI is required");
Assert.notNull(method, "HttpMethod is required");
ClientHttpResponse response = null;
Object var14;
try {
ClientHttpRequest request = this.createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
this.handleResponse(url, method, response);
var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;
} catch (IOException var12) {
String resource = url.toString();
String query = url.getRawQuery();
resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;
throw new ResourceAccessException("I/O error on " + method.name() + " request for \"" + resource + "\": " + var12.getMessage(), var12);
} finally {
if (response != null) {
response.close();
}
}
return var14;
}
responseExtractor.extractData这个方法负责从httpresponse中获取数据,利用到了消息转换器。实际进入到类HttpMessageConverterExtractor中:
public T extractData(ClientHttpResponse response) throws IOException { MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response); if (responseWrapper.hasMessageBody() && !responseWrapper.hasEmptyMessageBody()) { MediaType contentType = this.getContentType(responseWrapper); try { Iterator var4 = this.messageConverters.iterator(); while(var4.hasNext()) { HttpMessageConverter<?> messageConverter = (HttpMessageConverter)var4.next(); if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<?> genericMessageConverter = (GenericHttpMessageConverter)messageConverter; //此处即是判断消息转换器的类型是否会http响应的类型相同 if (genericMessageConverter.canRead(this.responseType, (Class)null, contentType)) { if (this.logger.isDebugEnabled()) { ResolvableType resolvableType = ResolvableType.forType(this.responseType); this.logger.debug("Reading to [" + resolvableType + "]"); } return genericMessageConverter.read(this.responseType, (Class)null, responseWrapper); } } if (this.responseClass != null && messageConverter.canRead(this.responseClass, contentType)) { if (this.logger.isDebugEnabled()) { String className = this.responseClass.getName(); this.logger.debug("Reading to [" + className + "] as \"" + contentType + "\""); } return messageConverter.read(this.responseClass, responseWrapper); } } } catch (HttpMessageNotReadableException | IOException var8) { throw new RestClientException("Error while extracting response for type [" + this.responseType + "] and content type [" + contentType + "]", var8); } throw new UnknownContentTypeException(this.responseType, contentType, responseWrapper.getRawStatusCode(), responseWrapper.getStatusText(), responseWrapper.getHeaders(), getResponseBody(responseWrapper)); } else { return null; } }
通过红色标记的异常,正是我们程序输出的异常。不难发现,这个方法一开始就会获取所有的消息转换器,然后逐条遍历,判断是否有相应的转换器的类型与http响应类型responseType相同,如果都没有相同的,则会抛出该异常。到这里我们就会容易想到,既然原因是没有这个类型text/plain引起的,我们可以在转换器集合中添加这个类型。
两种解决方法:
1、我们可以仿造MappingJackson2HttpMessageConverter类,创建一个MyMappingJackson2HttpMessageConverter类,将里面的
super(objectMapper, new MediaType[]{MediaType.APPLICATION_JSON, new MediaType("application", "*+json")});
改为
super(objectMapper, new MediaType[]{MediaType.TEXT_PLAIN, new MediaType("text", "*+plain")});
2.也可以直接继承MappingJackson2HttpMessageConverter类,自定义MyMappingJackson2HttpMessageConverter类,在其构造方法中添加Text_PLAIN类型
public class MyMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter { public WxMappingJackson2HttpMessageConverter(){ List<MediaType> mediaTypes = new ArrayList<>(); mediaTypes.add(MediaType.TEXT_PLAIN); setSupportedMediaTypes(mediaTypes); } }
最后在我们的springboot项目中构建RestTemplate对象的地方,添加我们自定义的消息转换器
@Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder builder){ RestTemplate restTemplate = builder.build(); restTemplate.getMessageConverters().add(new WxMappingJackson2HttpMessageConverter()); return restTemplate; } }
到此,分析完毕。
借鉴了下面博主的这个分析,自己也重新做了分析验证和其它的解决方法,不过殊途同归,有不正确的地方还请大佬不吝指教。RestTemplate请求Could not extract response: no suitable HttpMessageConverter found for response type.._z69183787的专栏-CSDN博客