《經久不衰的Spring框架:@ResponseBody 中文亂碼》

問題背景

  本文並非介紹@ResponseBody註解,也不是中文亂碼問題的大彙總筆記,這些網上都有不少內容了。這邊僅對幾年前,一個卡殼了挺久時間的問題的解決過程作一個記錄,以警戒本身,達到自醒得目的。  html

  @ReponseBody 註解不用多介紹了,用過SpringMVC的同窗都很熟了,@ResponseBody 將內容或對象做爲 HTTP 響應正文返回,使用@ResponseBody將會跳過視圖處理部分,而是調用適合的HttpMessageConverter,將返回值寫入輸出流。在平常工做中,一般使用封裝好的ViewModel進行後臺數據的返回,一切正常。但一次在使用@ReponseBody進行返回String數據的時候,竟會出現中文亂碼。web

  編程的過程免不了遇到各類問題,而遇到問題而後解決問題的這個過程我認爲是最讓人興奮的事情。越棘手的問題,解決之後帶來的快感也越大(PS:固然解決不了的話,就會越煩躁。。),仍是言歸正傳,談一下解決錯誤的過程。spring

問題分析

  最先我一直覺得Spring配置一下編碼過濾器就能夠解決任何中文亂碼問題,代碼以下: 恩,確實一直是這樣設置着,然並卵。編程

<filter>
  <filter-name>characterEncodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
 

  直接返回String會亂碼,而返回ViewModel的那個不會亂碼,這是爲何?mvc

  其實也能夠說是SpringMVC的一個bug,SpringMVC有一系列HttpMessageConverter去處理用@ResponseBody註解的返回值,如返回VM則使用MappingJacksonHttpMessageConverter,若返回String,則使用StringHttpMessageConverter,這個convert使用的是字符集是iso-8859-1,並且是final的,以下:app

public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); 

  既然是String有問題,那天然就直接從適配器AnnotationMethodHandlerAdapter 的字符串解析器 StringHttpMessageConverter 入手,設置編碼類型便可?編碼

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> 
  <property name="messageConverters"> 
   <list> 
    <bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
     <property name="supportedMediaTypes"> 
      <list> 
       <value>text/plain;charset=UTF-8</value> 
      </list> 
     </property> 
    </bean> ...

  NO!url

      那萬金油response.setContentType("text/html; charset=UTF-8");呢?NO!spa

  那麼重寫StringHttpMessageConverter應該能夠了嗎?NO!code

  上面方法都不行,就嘗試着各類百度,說法多種多樣,但答案仍是:NO!

問題解決

  恩,看來咱們得從源頭開始再理一遍,既然問題在解析器,那麼能夠從配置文件配置解析器的配置文件入手。mvc-config.xml 文件從上到下:控制層掃描、國際化配置、文件上傳表單解析器、自定義攔截器、視圖配置。好像都不是,繼續往下,一些HandlerMapping和HandlerAdapter,還有一句<mvc:annotation-driven/>。

  網上查閱了一下資料,果真發現問題其實就在這句<mvc:annotation-driven/>。

  <mvc:annotation-driven /> 是一種簡寫模式,它會自動註冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter 兩個bean,是spring MVC爲@Controllers分發請求所必須的,而且提供了其餘一些支持。。。略

  上面使用爲StringHttpMessageConverter設置編碼模式其實正常是有效的,可是在使用了<mvc:annotation-driven />語句後,再次顯示聲明其餘bean,可能就無效了。

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8" />
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

擴展:其餘解決方案

  除了使用上面那種方案以外,還可使用下面的: 

  一、徹底不使用String返回,直接都經過統一的ViewModel去返回,如ResultModel等(正常也是這麼幹的,總還要錯誤信息等吧);
  二、經過@RequestMapping的屬性處理,該註解的屬性produces用於指定返回的內容類型,這算確定能夠了,代碼以下:

@RequestMapping(value="/test", method=RequestMethod.POST, produces="text/html;charset=UTF-8")

  注意:既然使用了配置<mvc:annotation-driven>,仍是建議在該配置內部進行處理。

編後語

   上面的問題記錄是不少多年前的了,如今翻到博客上,只是爲了告誡本身:

  天下文章一大抄,可是抄來抄去,不論是寫的人,仍是看的人,本身最好都能理解或者親自去嘗試一下;這個亂碼問題網上總結太多太多了,不能說是錯誤的,可是又有幾個正確得說清楚呢?

  若是放在如今來解決這個問題,最好的方案仍是直接跟一下SpringMVC的源碼,簡單明瞭。String類型的返回值處理會進入StringHttpMessageConverter解析器,觀察getContentTypeCharset方法的參數,能夠看到當前解析編碼是text/plain;charset=ISO-8859-1,即設置不成功,設置不成功能夠繼續看看適配器AnnotationMethodHandlerAdapter的屬性設置,一層層反推,總會發現問題的。

  這裏奉勸各位兄弟姐妹,網上的東西仍是要仔細研究的,不要轉來轉去的。

相關文章
相關標籤/搜索