Apache HttpClient 讀取響應亂碼問題總結
Apache HttpClient 讀取響應亂碼問題總結
setCharacterEncoding Content-Type HttpClient起因
最近公司產品線研發人員調整,集中兵力做戰略產品,現在穩定產品叠代放慢。新的產品線當前有一個最初的版本,為了盡快了解業務,以 API 為入口,以 API 測試為手段,梳理當前版本的業務流程。
在通過 HttpClient 對 API 進行訪問時,發現返回的字符串中包含的中文為亂碼
環境
- JDK 1.8
- Servlet 3.0.x
- HttpClient 4.2.1
排查
疑因1
由於我們對 HttpClient 進行了再次封裝,封裝中使用 UTF-8 對響應數據進行編碼,之前其它產品線也都使用此 Jar 進行後端 Http API 訪問,均未出現亂碼的情況,所以初步懷疑是服務沒有設置響應編碼為 UTF-8
HttpClient 封裝代碼如下:
HttpEntity entity = response.getEntity();
if (code >= 200 && code < 400) {
return EntityUtils.toString(entity, Charset.forName("UTF-8"));
} else {
EntityUtils.consume(entity);
throw new IllegalArgumentException("請檢查連接是否正確,http return code=" + code);
}
查看服務端代碼,發現設置了響應的字符編碼
response.setCharacterEncoding("UTF-8" );
這樣服務端編碼與客戶端編碼都是 UTF-8,理論上不應出現亂碼的情況。
繼續看代碼,發現後端代碼沒有設置響應的 Content-Type,加上如下代碼:
response.setContentType("application/json;charset=utf-8");
再次進行測試,發現中文顯示正常。
疑因2
排除掉疑因1後,懷疑 setContentType() 方法和 setCharacterEncoding() 方法的處理不一致,遂查看源碼,如下:
enter description here
發現 setContentType 方法內部也會調用 setCharacterEncoding 方法,唯一的區別就是 setContentType 方法設置了 Content-Type 頭信息
疑因3
排除掉2後,懷疑是 HttpClinet 對 ContentType 的處理有問題,並且 EntityUtils.toString(entity, Charset.forName("UTF-8"));
中的編碼沒有起作用。
查看其源碼,如下:
圖1
圖2
從圖1中可以看出來,如果沒有設置響應 ContentType,它會設置一個默認的 ContentType,從圖2中可以看來,設置的默認
Content-Type : text/plain;Charset=ISO-8859-1
Httpclient 會優先使用 ContentType 的編碼,只在 ContentType 編碼取不到的情況下,才會使用傳入的編碼(defaultCharset),而默認的 ContentType 始終帶有編碼(ISO-8859-1)。
所以,當服務端未顯式設置 ContentType 時,Httpclient 會使用 ISO-8859-1 編碼格式對響應數據進行編碼,而不是顯式傳入的 UTF-8 編碼,所以中文會出現亂碼。
總結
問題找到原因了,就好辦了。
我們只需要顯示設置服務端響應 Content-Type 即可,而且這樣可以避免通過瀏覽器訪問接口時出現亂碼,兼容性更好。
個人認為這是 HttpClient 的一個 bug ,本想給官方提個 issue,但沒找到提 Bug 的入口(笑哭),如有人知道,煩請告知,不勝感激!
Apache HttpClient 讀取響應亂碼問題總結