之前項目都是使用 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;
}
}