關(guān)于SpringMVC向前端返回?cái)?shù)據(jù)亂碼及相關(guān)問題解決過程記錄

問題背景

使用springMVC框架,可以在Controller方法的注解中,指定返回?cái)?shù)據(jù)的格式(xml、html、key-value、json等),但是spring默認(rèn)的消息轉(zhuǎn)換器的默認(rèn)編碼標(biāo)準(zhǔn)是ISO-8859-1,該編碼是西歐編碼,無法對(duì)中文編碼,所以,前端收到數(shù)據(jù)之后,按照響應(yīng)頭中的編碼(ISO-8859-1)去解碼,關(guān)于中文的內(nèi)容都會(huì)得到亂碼,即使我們手動(dòng)采用UTF-8,也不能解碼。

關(guān)于亂碼的幾種解決方案

如何給springMVC框架指定一個(gè)編碼呢?網(wǎng)上的大部分的說法是開頭提到的:在controller的@RequestMapping頭中,定義produces屬性,比如:

@RequestMapping(value = "/path/map" produces = "application/json; charset=utf-8">

但是,按照我初學(xué)springMVC的資料,Demo里一開始就是這么寫的,初學(xué)者也不會(huì)去想為什么springMVC要采用json傳輸、能不能用別的個(gè)數(shù)傳輸數(shù)據(jù)
在前端請(qǐng)求數(shù)據(jù),后臺(tái)在控制臺(tái)打印的DEBUG信息里有一行:

Written [ json串 ] as "text/plain;charset=ISO-8859-1" using org.springframework.http.converter.StringHttpMessageConverter@34da3a54]

相繼搜索下述關(guān)鍵詞:

  • Written xxx as "text/plain;charset=ISO-8859-1" using StringHttpMessageConverter
  • springMVC json 亂碼

找到了配置編碼的方法:除了要在Controller中注釋以外,還需要在springMVC的相關(guān)配置文件(xml)中進(jìn)行配置。
我的配置文件分為三個(gè):spring-dao.xml、spring-service.xml和spring-web.xml文件,分別用于將springMVC與DAO層框架Mybatis整合、spring與service層整合、springMVC與web項(xiàng)目整合。所以,我的項(xiàng)目中,關(guān)于消息轉(zhuǎn)換器的配置,應(yīng)該在spring-web.xml文件中進(jìn)行配置。
按照網(wǎng)上的說法(排除掉關(guān)于GET、POST方法中含有中文,服務(wù)端收到亂碼的回答和博客):

說法一:

`<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>`

說法二:

<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <array> <bean class = "org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" /> </bean> </array> </property> </bean>
參考來源:https://stackoverflow.com/questions/3616359/who-sets-response-content-type-in-spring-mvc-responsebody

說法三:

<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
參考來源:http://blog.csdn.net/stationxp/article/details/38775295

說法四:

<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json</value> <value>text/html;charset=UTF-8</value> </list> </property> </bean> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean>
參考來源:http://blog.csdn.net/jiaotuwoaini/article/details/51487605

最不靠譜的一種說法是,修改StringHttpMessageConverter的實(shí)現(xiàn),并且打包成jar。這種做法能夠暫時(shí)解決亂碼的問題,但是后期要是升級(jí)springMVC的版本,問題會(huì)再度出現(xiàn)。此外,對(duì)于Maven管理依賴的項(xiàng)目,移植到別的機(jī)器上,就會(huì)變成原生springMVC的jar包(如果修改的包不同名,那么就會(huì)找不到依賴)。所以這個(gè)不是解決方案。

上述方案的分析與嘗試

上述說法大致有兩個(gè)方案,一個(gè)是關(guān)于mvc:annotation-driven,一個(gè)是直接配置一些bean。但是,按照第一種方式配置之后,初始化的時(shí)候就會(huì)拋異常。第二種方式也不起作用,應(yīng)該是缺少什么。
第一種方式的異常一般都是:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'workItemsSubmitController' method public java.lang.Object imp4sep.controller.WorkItemsSubmitController.submitEGPAdd(int,java.util.List<imp4sep.po.EngineeringGeophysicsInfo>,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) to {[/work/submit/GDSP/add],methods=[POST],produces=[application/json;charset=utf-8]}: There is already 'workItemsSubmitController' bean method public java.lang.Object imp4sep.controller.WorkItemsSubmitController.submitGDSPAdd(int,java.util.List<imp4sep.po.GeologicalDrillingSamplingInfo1>,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) mapped.

尋找異常的原因

為了確定導(dǎo)致問題的原因,我相繼把mvc:annotation-driven標(biāo)簽的子標(biāo)簽和屬性移除,到最后,只要配置了<mvc:annotation-driven/>,就會(huì)拋這個(gè)異常。
搜索下述關(guān)鍵詞:

  • 開啟mvc:annotation-driven 導(dǎo)致BeanCreationException RequestMappingHandlerMap
  • Ambiguous mapping. Cannot map 'xxx' method xxx to xx: There is already 'xx'

一些博客記錄了相同的問題,一般是初學(xué)者的demo項(xiàng)目,發(fā)現(xiàn)是Controller的映射路徑重復(fù),移除其中一個(gè)controller。我沒有在意。
參考來源:http://www.cnblogs.com/davidwang456/p/4387654.html
還有說法:
bean重復(fù)初始化:使用@controller注解初始化一次,在applicationContext中又定義一次
參考來源:http://www.fanfanyu.cn/news/staticpagefile/2351.html

XML校驗(yàn)報(bào)錯(cuò)

在這中間還出現(xiàn)了一個(gè)小插曲:spring-*.xml文件相繼校驗(yàn)出錯(cuò)。
cvc-complex-type.2.4.c: 通配符的匹配很全面, 但無法找到元素 'mvc:annotation-driven'

在搜索報(bào)錯(cuò)信息的過程中找到了意外一個(gè)博客


<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean id="stringConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> </list> </property> </bean>
``
<mvc:annotation-driven />
參考來源:http://www.cnblogs.com/feiyujun/p/6581683.html

這篇博客的最后還提到springMVC版本的的問題,以至于后面我在找到問題真正所在前,開始懷疑高版本的配置文件與低版本不同。
此外,介紹的配置方式能夠解決亂碼問題,但是其中強(qiáng)調(diào)的配置順序卻并不能解決IE提示下載的問題。

在一個(gè)springMVC的QQ群里問了亂碼的問題,有三個(gè)群友給出兩個(gè)不同的配置:

  1. 配置一:
配置方法一

可能他的是低版本的配置,我將MappingJacksonHttpMessageConverter改成MappingJackson2HttpMessageConverter了:

<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8" /> <property name="writeAcceptCharset" value="false" /> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> <value>text/html;charset=UTF-8</value> <value>application/json;charset=UTF-8</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>

  1. 配置二

<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value="text/plain;charset=UTF-8" /> </bean> </mvc:message-converters> </mvc:annotation-driven>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="mappingJacksonHttpMessageConverter" /> </list> </property> </bean>
<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes"> <list> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="text" /> <constructor-arg index="1" value="plain" /> <constructor-arg index="2" value="UTF-8" /> </bean> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="*" /> <constructor-arg index="1" value="*" /> <constructor-arg index="2" value="UTF-8" /> </bean> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="text" /> <constructor-arg index="1" value="*" /> <constructor-arg index="2" value="UTF-8" /> </bean> <bean class="org.springframework.http.MediaType"> <constructor-arg index="0" value="application" /> <constructor-arg index="1" value="json" /> <constructor-arg index="2" value="UTF-8" /> </bean> </list> </property> </bean>

發(fā)現(xiàn)錯(cuò)誤的根本原因

嘗試了兩天仍然沒有解決的亂碼問題,今天我仔細(xì)看了一下異常信息,由于方法名相似,之前一直以為異常的信息所說的模糊映射是由于“多處配置”所致,
原來真的存在重復(fù)的映射。這個(gè)問題根結(jié)的發(fā)現(xiàn),同時(shí)也說明一個(gè)問題:開啟注解驅(qū)動(dòng)之后,首次啟動(dòng)初始化的時(shí)候,會(huì)遍歷全部的映射路徑,確保不存在重復(fù)映射,否則會(huì)拋出BeanCreationException異常。

解決問題

找到存在的重復(fù)映射路徑,修改過來之后,重新發(fā)布

  1. 無亂碼的,但不能美化的輸出
    按照群友1給出的配置之后,前端發(fā)來請(qǐng)求,得到?jīng)]有亂碼的數(shù)據(jù),如下:


    firefox瀏覽器展示的樣式

    原始數(shù)據(jù)

    點(diǎn)擊美化輸出無效?!邦^”標(biāo)簽里的響應(yīng)頭信息:


    請(qǐng)求響應(yīng)頭信息
  2. 無亂碼且可以美化的輸出
    按照群友2所說的配置:


    firefox瀏覽器展示的樣式

    原始數(shù)據(jù)

    請(qǐng)求響應(yīng)頭信息
  3. 無亂碼,可美化,接受編碼類型多的方案

`<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/plain;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

校驗(yàn)會(huì)報(bào)錯(cuò):

eclipse校驗(yàn)xml約束

這種校驗(yàn)報(bào)錯(cuò)時(shí)有時(shí)無,據(jù)說校驗(yàn)時(shí),除了從網(wǎng)絡(luò)位置校驗(yàn),還會(huì)從本地的jar包中校驗(yàn),不知道eclipse是不是這么做的。

firefox瀏覽器展示的樣式

原始數(shù)據(jù)標(biāo)簽下,點(diǎn)擊“美化輸出”,能夠進(jìn)行排版:

原始數(shù)據(jù)

頭標(biāo)簽的響應(yīng)數(shù)據(jù)也不同:


Accept-Charset的編碼格式豐富
  1. 無亂碼、不能美化的輸出
    網(wǎng)上找到的原配置是錯(cuò)誤的,如下:

<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean lass="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes" value="application/json;charset=UTF-8" /> <property name="supportedMediaTypes" value="text/html;charset=UTF-8" /> </bean> <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"> <property name="supportedMediaTypes" value="application/json;charset=UTF-8" /> <property name="supportedMediaTypes" value="text/html;charset=UTF-8" /> </bean> </mvc:message-converters> </mvc:annotation-driven>

發(fā)布之后會(huì)拋異常:
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Multiple 'property' definitions for property 'supportedMediaTypes' Offending resource: file [D:\Apache\apache-tomcat-9.0.0.M18\webapps\imp4sep\WEB-INF\classes\spring\spring-web.xml] Bean '' -> Property 'supportedMediaTypes'
修改為下述形式才可以:

<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean lass="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="supportedMediaTypes" value="application/json;charset=UTF-8" /> </bean> <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"> <property name="supportedMediaTypes" value="text/html;charset=UTF-8" /> </bean> </mvc:message-converters> </mvc:annotation-driven>

HTTP頭是這樣的:

請(qǐng)求響應(yīng)頭信息

上述可行方案的對(duì)比

在IE下都會(huì)會(huì)產(chǎn)生下載提示

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容