1. 問題復(fù)現(xiàn)
話不多說,先貼出問題代碼:這里的GetUserInfoByAccessToken
是我自定義的一個(gè)實(shí)體類。
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,并且服務(wù)端響應(yīng)報(bào)文的content-type為text/plain的情況下找不到一個(gè)合適的HttpMessageConverter
來處理這種情況
2. 處理方法
這里舉例兩種處理請(qǐng)求
1.首先StringHttpMessageConverter
這個(gè)處理器是可以處理content-type為text/plain的響應(yīng)報(bào)文的。但閱讀源碼知道必須放回類型是String才可以使用它,所有我們需要改寫下代碼,將放回類型改為String。需要的時(shí)候可以利用JSON
工具類將其轉(zhuǎn)為你需要的類型。
GetUserInfoByAccessToken getUserInfoByAccessTokenString = restTemplate.getForObject(userInfoByAccessCodeURL, String.class);
需要注意的是使用StringHttpMessageConverter
容易出現(xiàn)中文亂碼的情況,因?yàn)樗J(rèn)支持的字符集是ISO-8859-1
,這種時(shí)候可以參考以下代碼更改StringHttpMessageConverter
的默認(rèn)字符集,我這里將其改為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
的轉(zhuǎn)換器里再加一個(gè)支持JSON
轉(zhuǎn)換的轉(zhuǎn)換器,比如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 關(guān)鍵代碼extractData方法
extractData
方法將接口請(qǐng)求拿到的響應(yīng)報(bào)文拿來給HttpMessageConverter
解析,這里會(huì)找到合適的解析器來解析響應(yīng)報(bào)文,解析成我們指定的返回類型的數(shù)據(jù),如果找不到或者處理出現(xiàn)異常就會(huì)拋出異常。
// 這里的入?yún)⑹钦?qǐng)求之后的響應(yīng)體
public T extractData(ClientHttpResponse response) throws IOException {
//創(chuàng)建一個(gè)名為responseWrapper的MessageBodyClientHttpResponseWrapper,用于包裝響應(yīng)對(duì)象response,方便操作響應(yīng)數(shù)據(jù)。
MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response);
// 檢查響應(yīng)是否有消息體,并且消息體不為空。如果不滿足條件,則返回null。
if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) {
return null;
}
// 獲取響應(yīng)內(nèi)容類型contentType。
MediaType contentType = getContentType(responseWrapper);
try {
// 遍歷已注冊(cè)的HttpMessageConverter列表。
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
// 對(duì)于實(shí)現(xiàn)了GenericHttpMessageConverter接口的轉(zhuǎn)換器,檢查是否可以讀取responseType對(duì)應(yīng)的類型,并且內(nèi)容類型匹配。
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,則檢查是否有轉(zhuǎn)換器可以讀取該類型,并且內(nèi)容類型匹配。見相關(guān)代碼`canRead`方法中的代碼清單1-2
if (messageConverter.canRead(this.responseClass, contentType)) {
if (logger.isDebugEnabled()) {
String className = this.responseClass.getName();
logger.debug("Reading to [" + className + "] as \"" + contentType + "\"");
}
// 如果匹配成功,使用該轉(zhuǎn)換器讀取響應(yīng)數(shù)據(jù),并返回結(jié)果。
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 相關(guān)代碼messageConverter.canRead(this.responseClass, contentType)方法
canRead(java.lang.Class, org.springframework.http.MediaType)
方法判斷當(dāng)前的HttpMessageConverter
是否可以讀取響應(yīng)報(bào)文ContentType
為服務(wù)端指定的數(shù)據(jù),并且內(nèi)容和你指定的返回值類型匹配。
// 判斷`HttpMessageConverter`轉(zhuǎn)換器是否可以讀取該ContentType的數(shù)據(jù),并且內(nèi)容和你指定的返回值類型匹配
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
// supports判斷HttpMessageConverter轉(zhuǎn)換器是否支持你指定的返回類型,參考代碼清單1-3。canRead
return supports(clazz) && canRead(mediaType);
}
這是StringHttpMessageConverter
的supports方法,可以看出他可以處理返回類型為String的數(shù)據(jù)。
public boolean supports(Class<?> clazz) {
return String.class == clazz;
}
上面代碼supports方法返回true會(huì)調(diào)用canRead(org.springframework.http.MediaType)
方法,這段代碼主要就是判斷當(dāng)前的HttpMessageConverter
是否可以處理content-type為服務(wù)端指定類型的響應(yīng)報(bào)文,比如content-type為text/plain。
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
4.關(guān)鍵點(diǎn)截圖
以下是我在調(diào)試中截取的一些圖片。
這里可以看到響應(yīng)體的contentType
為text/plain,接下來就要找可以處理這種響應(yīng)類型的HttpMessageConverter
。
這里可以看到已注冊(cè)的HttpMessageConverter
列表里面有九個(gè)元素,并且通過他們的supportedMediaTypes
屬性看到他們可以處理的contentType
。
首先判斷HttpMessageConverter
是否可以讀取我們指定的返回類,這里我指定的是我自定義的一個(gè)返回類GetUserInfoByAccessToken.class
在這里是在判斷當(dāng)前HttpMessageConverter
是否可以處理當(dāng)前響content-type為text/plain的響應(yīng)報(bào)文。文章來源:http://www.zghlxwxcb.cn/news/detail-624474.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-624474.html
到了這里,關(guān)于Could not extract response: no suitable `HttpMessageConverter` found for response type [class wechat.xx] and content type [text/plain] 問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!