Could not extract response: no suitable `HttpMessageConverter` found for response type [class wechat.xx] and content type [text/plain] 問題

来源:https://www.cnblogs.com/yuyiming/archive/2023/08/04/17607286.html
-Advertisement-
Play Games

## 1. 問題復現 話不多說,先貼出問題代碼:這裡的`GetUserInfoByAccessToken`是我自定義的一個實體類。 ``` GetUserInfoByAccessToken getUserInfoByAccessTokenString = restTemplate.getForObj ...


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的響應報文。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在JavaScript語言里有個 Math.random() 隨機函數,用於生成指定範圍內的隨機數。 #### Math.random()函數 根據官方的定義: **Math.random()** 函數返回一個浮點數, 偽隨機數在範圍[0,1),也就是說,從0(包括0)往上,但是不包括1(排除1), ...
  • 一、js有如下:1、string類型;2、number類型;3、boolean類型;4、null類型;5、undefined類型;6、Object類型;7、Array類型;8、Function類型;9、Symbol類型。共九種數據類型。js把數據類型分為“基本數據類型”和“引用數據類型”。其中6、7 ...
  • 1、父傳子( 定義:父傳子使用的是 props ) ① 首先確定父組件和子組件,然後在父組件中引入子組件,註冊並使用; 父組件代碼如下: <template> <div> <h1>父組件</h1> <!-- 使用子組件 --> <ChildView></ChildView> </div> </tem ...
  • vue3引入了Composition API,使開發者能夠更靈活組織和重用組件邏輯。採用了基於Proxy的響應式系統,對虛擬DOM進行了優化等,提升了開發體驗和性能。 ...
  • 1.查看分支 查看本地分支 git branch 查看遠程分支 git branch -r 查看本地和遠程分支 git branch -a 2.創建分支 使用以下命令創建一個本地分支 git branch <本地分支名> 使用以下命令創建一個本地分支且新建分支從特定分支拉取代碼 git branch ...
  • ## 1. scope 概念 maven 在引入依賴時,配置上有一個 scope 標簽,例如: ```xml com.mysql mysql-connector-j 8.1.0 runtime ``` 例子中的 `runtime` 表示**運行時**的依賴範圍,不同的 scope 對於項目在編譯,測 ...
  • 本節內容的概要如下; 對象已死嗎? 一、判斷對象是否存活的演算法 1、引用計數器演算法 給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器為0的對象就是不可能再被使用的。 客觀地說,引用計數演算法(Reference Counting)的實現簡 ...
  • ## 5.1、bean的作用域 ### 5.1.1、單例(預設且常用) #### 5.1.1.1、配置bean ![image](https://img2023.cnblogs.com/blog/2052479/202308/2052479-20230803010539572-840709484.p ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...