【JavaWeb】 详解Servlet 中解决中文乱码的两个核心API

20 阅读3分钟

1.出现中文乱码的原因

HTTP 协议本身没有默认指定统一的编解码字符集

  1. 前端向服务器发送中文参数时,会用某一种字符集(如 UTF-8)编码成二进制数据传输;
  2. 服务器接收后,若用不同的字符集解码,就会出现乱码(如???、当前用户);
  3. 服务器向客户端返回中文响应时,若编码字符集与客户端解码字符集不一致,同理也会出现乱码。

而只要前后端字符集统一为 UTF-8,就能彻底解决中文乱码。

2. HttpServletRequest.setCharacterEncoding ("UTF-8")

为当前 HTTP 请求设置 请求体 的解码字符集为 UTF-8。前端把中文编码成二进制传给服务器,服务器用这个方法指定用 UTF-8 把二进制转成中文字符串。

注意点:

  1. 仅作用于请求体,不处理 URL 中的参数。

该方法只对POST请求有效(POST请求的参数存放在请求体中,如表单提交、AJAX POST 传参); 对 GET 请求完全无效(GET 请求的参数拼接在URL中)。

  1. 必须在获取请求参数之前调用。

如果先执行request.getParameter("name")request.getReader()等获取请求数据的操作,再调用setCharacterEncoding("UTF-8"),方法会完全失效(服务器已用默认字符集完成解码,后续设置无意义)。

  1. 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 -->
  1. 前端必须用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-Typecharset,客户端可能用默认字符集(如 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>