前些日子了一個新需求,需要在后臺使用http通訊。Spring 的RestTemplate的極簡風格讓我迷戀上了它,雖然項目中沒使用過,但是還是大膽的使用了它,因為它出身名門【spring家族】。
1. 第一次遇到亂碼
使用RestTemplate代碼很簡潔,很快就寫完了第一個請求。以下使用的是自己對RestTemplate 的封裝類。
@RequestMapping(value = "/request",produces="application/json;charset=UTF-8")
public String request(@RequestBody(required = false) String requestBody, @RequestHeader HttpHeaders headers){
String json = null ;
Map<String,Object> map = new HashMap<String,Object>();
try {
String requestURL= headers.getFirst("url");
json=HttpClient.postRest(requestURL,requestBody,headers);
} catch (Exception e) {
logger.error("remote request error ",e);
map.put("code",-1);
map.put("msg","請求失敗");
return JSON.toJSONString(map);
}
logger.info("response json : {}",json);
return json;
}
第一次請求就亂碼了,亂碼如下:

2. 請求恢復正常
后來發(fā)現我們使用了自己定義的假域名,本地沒有配置hosts,于是我加上了hosts配置,就恢復正常了。
3. 發(fā)布線上再次出現亂碼
我們在自己測試環(huán)境一切都很順利,很快就可以發(fā)布上線了。
最后一個服務發(fā)布完畢后開始驗證,馬上就出現了和第一次亂碼一樣的問題。
于是我記起來我們在測試環(huán)境的時候是因為我們沒有配置host的情況,查了線上hosts情況。
又感覺不對勁,線上是域名是真是的域名,應該是不需要配置hosts的。
后面就各種百度,希望有相同的案例。
搜索到的大多數問題是StringHttpMessageConverter 的默認編碼為ISO8859-1導致的亂碼。只要把StringHttpMessageConverter 配置成UTF-8或在Header中配置contentType為UTF-8即可解決. 我們這么做了然而沒有任何效果。
4.使用 Apache HttpComponents 替換了 Spring RestTemplate
這種突發(fā)狀況暫時沒能解決,然而系統(tǒng)一定是要發(fā)布的。我們只好把RestTemplate替換成了 Apache 經典的 httpclient組件,然后一切恢復正常。上線成功!
5.RestTemplate 亂碼的根本原因
這次發(fā)布的亂碼問題有同事覺得是RestTemplate的一個bug(我也是這么認為的)。于是我決定找出這個bug。第一個要做的就是復現線上出現的問題。
我在線上機器構建了一個測試的服務,在本地發(fā)送請求,果然復現了那個亂碼問題。然而同樣的代碼發(fā)布到測試環(huán)境卻一點問題也沒有。
經過排查發(fā)現是我們線上Nginx服務配置 gzip壓縮,我們測試環(huán)境并沒有配置。所以我們看到的亂碼其實是gzip格式轉String之后的樣子,大家再看一下。

網上說的亂碼其實只是中文亂碼。而我們當時疏忽了這一點。

知道問題就好辦啦,我使用JUnit單元測試去掉了header中的gzip。

果然! 所謂的亂碼就消失了。
此次亂碼的原因太隱蔽,我們把客戶端傳過來的header直接用來發(fā)送給遠程服務,忽視了header中的【gzp參數】,剛好測試環(huán)境沒配置gzip,線上配置了gzip。測試那么順利,已發(fā)布就懵逼了!