1.出现中文乱码的原因
HTTP 协议本身没有默认指定统一的编解码字符集:
- 前端向服务器发送中文参数时,会用某一种字符集(如 UTF-8)编码成二进制数据传输;
- 服务器接收后,若用不同的字符集解码,就会出现乱码(如???、å½åç¨æ·);
- 服务器向客户端返回中文响应时,若编码字符集与客户端解码字符集不一致,同理也会出现乱码。
而只要前后端字符集统一为 UTF-8,就能彻底解决中文乱码。
2. HttpServletRequest.setCharacterEncoding ("UTF-8")
为当前 HTTP 请求设置 请求体 的解码字符集为 UTF-8。前端把中文编码成二进制传给服务器,服务器用这个方法指定用 UTF-8 把二进制转成中文字符串。
注意点:
- 仅作用于请求体,不处理 URL 中的参数。
该方法只对POST请求有效(POST请求的参数存放在请求体中,如表单提交、AJAX POST 传参); 对 GET 请求完全无效(GET 请求的参数拼接在URL中)。
- 必须在获取请求参数之前调用。
如果先执行request.getParameter("name")、request.getReader()等获取请求数据的操作,再调用setCharacterEncoding("UTF-8"),方法会完全失效(服务器已用默认字符集完成解码,后续设置无意义)。
- GET方式请求URL参数的乱码处理:
因为setCharacterEncoding("UTF-8") 对GET无效,若GET请求的URL中文参数乱码,需修改Web 容器(如 Tomcat)的配置,指定URL解析的字符集为UTF-8:
- Tomcat 8+ :默认已使用 UTF-8 解析 URL,无需配置;
- Tomcat 7 及以下:修改Tomcat/conf/server.xml,在 Connector 节点添加
URIEncoding="UTF-8"
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/> <!-- 新增这行,指定URL参数解码为UTF-8 -->
- 前端必须用UTF-8 编码请求数据
该方法是服务器端的配置,要求前端必须用UTF-8 编码请求数据(如表单设置accept-charset="UTF-8"、AJAX 设置contentType="application/x-www-form-urlencoded;charset=UTF-8"),否则前后端编码不一致,仍会乱码。
3.HttpServletResponse.setCharacterEncoding ("UTF-8")
为当前 HTTP 响应设置响应体(Response Body) 的编码字符集为 UTF-8。服务器把中文响应数据(如页面内容、JSON、提示文字)用 UTF-8 编码成二进制,传给客户端解析。
1)仅作用于响应体,与响应头无关
HTTP 响应头的编码由 HTTP 协议默认指定(ISO-8859-1),该方法只处理响应体的内容(如 Servlet 输出的 HTML、JSON 数据),响应头的中文处理需单独转码(极少场景需要)。
(2)在获取响应输出流 / 打印流之前调用
若先执行response.getWriter()、response.getOutputStream()获取输出对象,再调用setCharacterEncoding("UTF-8"),方法失效,服务器会用默认字符集编码响应体。
(3)建议设置:response.setContentType()
实际开发中,很少单独调用该方法,因为response.setContentType("text/html;charset=UTF-8")或response.setContentType("application/json;charset=UTF-8")会同时完成两个操作:
- 调用setCharacterEncoding("UTF-8"):设置响应体编码;
- 设置Content-Type响应头:告诉客户端用 UTF-8 解码响应体。
只调用setCharacterEncoding("UTF-8"),若不设置Content-Type的charset,客户端可能用默认字符集(如 GBK)解码,仍会乱码。
4.应用Spring/SpringMVC框架
实际开发中,我们很少在原生 Servlet 中手动调用这两个方法,Spring/SpringMVC 提供了全局乱码过滤器CharacterEncodingFilter,一次性配置即可对所有请求 / 响应生效,底层就是封装了这两个方法:
<!-- web.xml中设置全局中文乱码过滤器:底层自动调用两个setCharacterEncoding方法 -->
<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>
<!-- 强制响应编码为UTF-8 -->
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!-- 对所有请求生效 -->
<url-pattern>/*</url-pattern>
</filter-mapping>