AJAX請求在使用@RequestMapping時返回中文亂碼
情況描述:使用SSM框架的專案,需要使用ajax請求並返回資料,但是設定所有伺服器端和瀏覽器客戶端的編碼都同一為:UTF-8結果還是亂碼。
原因分析:(網上基本都是一致的答案)
首先: 確定的是(經過多次測試的結果)只有當返回值是 String時才會出現中文亂碼,而當返回值是Map<String, Object>
或者是其它型別時,並沒有中文亂碼的出現.
然後找原因: 原因是這可以說是spring mvc的一個bug,spring MVC有一系列HttpMessageConverter去處理用@ResponseBody註解的返回值,如返回list或其它則使用 MappingJacksonHttpMessageConverter
StringHttpMessageConverter
,而這個convert使用的是字符集是iso-8859-1,而且是final的。所以在當返回json中有中文時會出現亂碼。
public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
解決辦法(以及是否嘗試成功): 嘗試了很多種網上的辦法,有一些根本無用,有一些當客戶端的Accep是 application/json;時無用.最終也是結合客戶端的修改才成功解決問題的。
環境: SpringMvc 3.1
客戶端分為三種不同的請求:
1.瀏覽器中直接Get訪問,Accept是”text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/
2.用Jquery的jsonp ajax請求,Accept是”/”
3.用h5+環境下的mui ajax請求,Accept是”application/json;charset=utf-8”
(PS:h5+是最近做的跨平臺移動專案的開發環境,另外就是有試過預設的Accept”application/json”的話不管伺服器端用哪種方法,在呢麼配置,都會返回亂碼,所以最後只得手動加上charset=utf-8了)
注: 如果什麼都不加,第一種和第二種都是返回的亂碼,第三種由於手動要求了返回格式,所以沒有亂碼,這裡之所以要第三組做對比是因為有一些方法會造成第三組不能正常訪問。
嘗試方法一
<mvc:annotation-driven >
<!-- 訊息轉換器 -->
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/html;charset=UTF-8"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
原理: StringHttpMessageConverter的父類裡有個List supportedMediaTypes屬性,用來存放 StringHttpMessageConverter支援需特殊處理的 MediaType 型別,如果需處理的 MediaType 型別不在 supportedMediaTypes列表中,則採用預設字符集。
最終結果:
請求方法1中文亂碼
請求方法2返回正確的中文
請求方法3返回正確的中文
嘗試方法二: 在配置檔案中的mvc:annotation-driven中新增如下程式碼
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<span style="white-space:pre"> </span> <value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
<value>*/*;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
原理: 原理同上,但是這裡多加了幾個value。
最終結果:
請求方法1中文亂碼
請求方法2返回正確的中文
請求方法3返回正確的中文
關於這種型別的方法為什麼不能正確解決,原因網上都是各不相同,這裡也不敢輕易做出結論.總之,目前來看這種方法不能解決問題
嘗試方法三: 在@RequestMapping裡的配置produces=”text/html;charset=UTF-8;”
@RequestMapping(value = “*“,produces=”text/html;charset=UTF-8;”)
原理: 手動給對應的Accept返回制定格式編碼資料。
最終結果:
請求方法1返回正確的中文
請求方法2返回正確的中文
請求方法3無法請求,出了錯,因為produces沒有新增application/json;對應的頭部.
嘗試方法四: 在@RequestMapping裡的併發配置produces={“application/json;”,”text/html;charset=UTF-8;”}
@RequestMapping(value = “*“,produces={“application/json;”,”text/html;charset=UTF-8;”})
原理: 手動給對應的Accept返回制定格式編碼資料。
最終結果:
請求方法1返回中文亂碼
請求方法2返回中文亂碼
請求方法3返回正確的中文
嘗試方法五: 在@RequestMapping裡的併發配置produces={“text/html;charset=UTF-8;”,”application/json;”}
@RequestMapping(value = “*“,produces={“text/html;charset=UTF-8;”,”application/json;”})
注意: 這裡和上個方法的區別是,produces裡面的順序對調了
原理: 手動給對應的Accept返回制定格式編碼資料。
最終結果:
請求方法1返回正確的中文
請求方法2返回正確的中文
請求方法3返回正確的中文
方法四和方法五對比分析:
發現produces設定多個Accept只有第一個的charset是有用的,
後面的Accept設定有效(因為不設定就無法接收對應的Accept請求),但是charset設定是無效的.需要客戶端手動制定charset才行.
具體原因並不清楚(原諒我並不精通)
所以得出的結論是:
produces={“text/html;charset=UTF-8;”,”application/json;”}
這樣設定,這樣普通瀏覽器的請求就能正常顯示中文,而客戶端的模擬請求(可以是ajax或http)則手動指定Accept的charset,即可正常接收中文。