1. 服务器端发送数据到客户端(如:浏览器)的乱码问题
在整个服务器端数据返回到浏览器的过程中,涉及到三次编码。
第一次:java文件以什么编码存放在硬盘中,一般工程全部使用UTF-8编码方式,所以程序中的中文字符是以UTF-8形式编码的 。
第二次:中文字符串是以什么编码方式转换成字节数组的,如果未指定@RequestMapping的produces属性,同时也未给StringHttpMessageConverter指定编码方式,最终中文字符串是以ISO-8859-1形式转换成字节数组的 。
第三次:数据发送给浏览器后,浏览器接收到一堆字节数组,浏览器又是以什么编码方式来解码的。 浏览器的解码方式由response的content-type决定。
这样才能保证不会乱码,首先java文件是以UTF-8形式存储的,然后指定StringHttpMessageConverter或者@RequestMapping的produces的编码方式为UTF-8,最后发给浏览器的header中的content-type也为UTF-8,这样才不会乱码。
流程分析:
假设服务器向客户端返回一个中文字符串,首先由RequestMappingHandlerAdapter来调度执行,由于是@ResponseBody,所以从所有的已注册的HandlerMethodReturnValueHandler中找到了@ResponseBody的支持者RequestResponseBodyMethodProcess。然后就是根据客户端Accept字段指定的多个content-type和服务器端指定的content-type进行比较配对,选出最合适的一个content-type。此时,如果@RequestMapping中并没有为produces指定相应的content-type,则会获取所有的已注册的HttpMessageConverter所支持的content-type作为服务器端指定的content-type。因为服务器返回的是字符串,所以最终会选出text/html作为最终的content-type,服务器端数据要以text/html形式写入response的body中。有了返回值的类型为String和content-type为text/html,然后就是从已注册的HttpMessageConverter中找到一个支持这两者的HttpMessageConverter,然后就找到了StringHttpMessageConverter,它有两个构造函数,一个可以指定字符集,当什么都没有指定时,默认使用ISO-8859-1。在将返回值以text/html形式写入response的body中时,StringHttpMessageConverter先从上述所选出的content-type(即text/html)中尝试获取字符集,若获取不到(因为没在构造函数中指定),则使用自己默认的ISO-8859-1,所以charset就为StringHttpMessageConverter默认的ISO-8859-1,造成了编码方式不对,同时ISO-8859-1是不支持中文的,所以就出现了乱码。
解决方式:指定@RequestMapping的produces为"text/html;charset=UTF-8"即可解决乱码。 此时StringHttpMessageConverter可以从这个content-type读取到编码方式,便不再采用默认的编码方式ISO-8859-1,并将返回的字节数组写入response的body中,同时设置response的content-type为produces的值即text/html;charset=UTF-8,浏览器拿到这个content-type便知道以UTF-8形式来解码这些字节数组。
注意:只在配置文件中指定StringHttpMessageConverter的编码方式(如下),是不能完全解决乱码问题的,此方式只会让StringHttpMessageConverter以指定的编码集(如:UTF-8)来转换成字节数组并写入resposne的body中。 但是,返回给浏览器的content-type字段并没有指定编码集,浏览器将以它默认的方式来解码(如果 浏览器的 解码方式不是utf -8就会造成乱码)。
<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>
2. 客户端到服务器端乱码问题
如:输入http://localhost:8080/test?name=中国,向服务器发送请求,整个过程涉及到2次编码:
第一次:浏览器将以什么样的编码方式将中文字符串转化成字节数组,这称为URL编码
第二次:当浏览器发送请求时,服务器是以请求的content-type来解析请求数据的,当浏览器请求没有指定content-type时,服务器又是采用什么样的编码来解析的 。服务器一般根据request的content-type来解析字符串,如果没有指定content -type 则采用服务器默认的编码方式解码。
乱码的本质:这两次编码方式不一致 。
1、针对第一个过程,当你仅仅在浏览器上输入http://localhost:8080/test?name=中国来访问时,不同的浏览器会采用不同编码方式来将“中国”转换成字节数组。比如说chrome浏览器始终以UTF-8的编码形式将中国转换成字节数组。而IE浏览器则是以GBK的编码方式来转换的,正是由于不同浏览器的不同处理情况,导致了可能用chrome发送服务器端正常,IE发送则乱码的现象。
2、针对第二个过程:由于未指定request的content-type来让服务器解析这些字节数组,它到底采用什么样的方式来解析呢,不同的服务器应该有不同的策略,并且可以进行设置。如Tomcat服务器,默认采用的是ISO-8859-1,可以修改Tomcat的conf/server.xml文件来修改Tomcat的默认编码解析方式。
这里的tomcat版本是7,在tomcat8中已修订,不存在这个乱码问题 。
因此要解决乱码问题就要指定客户端发送的request的content-type。