Spring3.0 MVC @ResponseBody 的作用是把傳回值直接寫到HTTP response body裡。
Spring使用AnnotationMethodHandlerAdapter的handleResponseBody方法, AnnotationMethodHandlerAdapter使用request header中"Accept"的值和messageConverter支援的MediaType進行比對,然後會用"Accept"的第一個值寫入response的"Content-Type"。一般的請求都是通過浏覽器進行的,request header中"Accept"的值由浏覽器生成。
有人跟蹤@ResponseBody 的實作類發現其預設的編碼是 iso-8859-1,
解決辦法,在spring mvc的配置檔案中手工配置bean:
<!-- 啟動Spring
MVC的注解功能,完成請求和注解POJO的映射 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
>
<property
name="messageConverters">
<list>
<bean class =
"org.springframework.http.converter.StringHttpMessageConverter">
<property name =
"supportedMediaTypes">
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
這樣通過配置AnnotationMethodHandlerAdapter類messageConverters屬性來指定編碼。
記住,需要把bean部分加入到<context:component-scan
base-package="com.zlscw.mvc" />前面,
這樣就可以在jquery中直接調用而不出現亂碼了。
-------------------------------------------這篇文章說的很到位
近日用Spring3的MVC寫東西,深感其之于Webwork/Struts2的便利,但是在通過@ResponseBody這個annotation
輸出一個json字元串的時候,發現頁面上獲得的json字元串中文字元出現了亂碼的現象。通過firefox觀察傳回的字元串,中文部分全部變成
了???????的形式,初步判定是傳回時,spring處理@ResponseBody使用了錯誤的編碼。
因為我在web.xml中已經配置了Spring的CharacterEncodingFilter,并且強制将request和response的編碼都指定為utf-8,是以出現亂碼的原因肯定是在Spring内部某處的邏輯了。
把log4j中關于spring的輸出級别調為debug,通過通路出問題的位址,發現Spring在處理@ResponseBody這個
annotation的時
候,org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter使用了org.springframework.http.converter.StringHttpMessageConverter進行處理,于是打開了Spring的源碼,看看這個類究竟做了哪些事情。
不看不要緊,一看吓一跳,裡面竟然是這樣定義其預設編碼的:
1publicstaticfinalCharset
DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
頓時心生N種不爽:堂堂Spring,竟然還在其中用西歐字元集作為其預設編碼,坑爹啊!(很多spring的類中,涉及編碼的已經都是utf-8了,比如負責JSON視圖的MappingJacksonHttpMessageConverter,就是預設使用UTF-8)。本來想直接修改spring的源碼重新打包一個jar出來,後來看spring的java
doc發現,其父類org.springframework.http.converter.AbstractHttpMessageConverter中的getDefaultContentType方法是可以重寫的:
By default, this returns the first element of the
supportedMediaTypes property, if any. Can be overridden in
subclasses.
心想這下就簡單了,你的DEFAULT_CHARSET不是final麼?那我自己繼承一個出來,按照我的需求定義為utf-8不就得了?代碼如下:
01publicclassUTF8StringHttpMessageConverter
extendsStringHttpMessageConverter { 02 03
privatestaticfinalMediaType
utf8 = newMediaType("text", "plain", 04
Charset.forName("UTF-8")); 05
privatebooleanwriteAcceptCharset
= true; 06 07 @Override08
protectedMediaType getDefaultContentType(String
dumy) { 09 returnutf8; 10 } 11 12
protectedList getAcceptedCharsets() { 13
returnArrays.asList(utf8.getCharSet()); 14 } 15 16
protectedvoidwriteInternal(String
s, HttpOutputMessage outputMessage) 17
throwsIOException { 18
if(this.writeAcceptCharset) { 19
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
20 } 21 Charset charset = utf8.getCharSet(); 22
FileCopyUtils.copy(s,
newOutputStreamWriter(outputMessage.getBody(), 23
charset)); 24 } 25 26
publicbooleanisWriteAcceptCharset()
{ 27 returnwriteAcceptCharset; 28 } 29 30
publicvoidsetWriteAcceptCharset(booleanwriteAcceptCharset)
{ 31 this.writeAcceptCharset = writeAcceptCharset;
32 } 33 34}
然後,在spring的配置檔案中添加如下bean聲明,用自己寫的類替換掉原有的StringHttpMessageConverter:
1"org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
2 "messageConverters"> 3 4
"utf8StringHttpMessageConverter"class="xxx.xxx.UTF8StringHttpMessageConverter"/>
5 6 7
再看通過@ResponseBody傳回的json字元串,終于中文都可以正常顯示了。
-------------------------------------------下面有一些解釋
但我們一般會在标注@ResponseBody的方法上傳回String或byte[]類型的結果,期望的"Content-Type"的值應為"text/plain"或"application/octet-stream"。
這樣導緻了浏覽器不能正确處理傳回的内容。
實際上Spring在用HttpMessageConverter處理的過程中首先會判斷response
header中有沒有寫入"Content-Type",如果沒有寫入的話才會使用request
header中"Accept"的第一個值。
但是由于Spring對HttpServletResponse進行了封裝,實際上使用的是ServletServerHttpResponse,這個類有一個對真正的HttpServletResponse的引用。
判斷response
header的過程中使用的是ServletServerHttpResponse的getHeaders()方法,但該方法并沒有傳回真正的HttpServletResponse中的header。(這應該有問題吧?)
是以我們雖然可以在Controller的方法中加入對HttpServletResponse的引用,然後設定"Content-Type"的值,但是并不會起作用。
來處理@ResponseBody,該類再使用一些HttpMessageConverter來具體處理資訊。
Chrome生成的值為application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,**
是以最後寫入response中"Content-Type"的值為"application/xml"或"application/x-ms-application"。
-------------------------------------------------其實這個注解完全可以不用,直接使用response往輸出流裡面寫。
使用jQuery ajax調用的傳回json,中文亂碼問題
Jquery :
$.ajax({
url: '/test/testAction.do?method=test',
type: 'POST',
dataType: 'json',
timeout: 5000,
async: false,
error: function(){
alert('擷取資料失敗!');
},
success: function(json){
jsObject = eval_r(json);
}
});
return jsObject;
JSONArray json =
JSONArray.fromObject(SysList);//SysList是一個List
// 設定response的ContentType解決中文亂碼
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print(json.toString());
return null;