0%

中文乱码问题

Java Web 中文乱码问题全解析:GET/POST 请求编码解决方案

中文乱码是 Java Web 开发中常见的问题,根源在于请求参数编码与解码方式不匹配。GET 和 POST 请求的参数传递方式不同,乱码原因和解决方案也存在差异。本文将详细分析中文乱码的成因,并提供针对性的解决方法,覆盖 Tomcat 配置、代码处理等多种场景。

中文乱码的本质原因

HTTP 协议默认使用 ISO-8859-1 编码(不支持中文),若客户端发送的中文参数使用 UTF-8 编码,而服务器端用 ISO-8859-1 解码,就会导致乱码(表现为 ??? 或一串无意义字符)。

  • GET 请求:参数附加在 URL 中,编码由服务器的 URI 编码配置决定;
  • POST 请求:参数放在请求体中,编码由 Content-Type 头的 charset 指定,若未指定则默认使用 ISO-8859-1。

GET 请求中文乱码解决方案

GET 请求参数通过 URL 传递,其编码处理依赖服务器配置(如 Tomcat 的 URI 编码设置),常见解决方案如下:

方案一:修改 Tomcat 配置(推荐)

在 Tomcat 的 conf/server.xml 中,为 Connector 节点添加 URIEncoding="UTF-8" 属性,强制服务器对 URI 中的参数使用 UTF-8 解码:

1
2
3
4
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" /> <!-- 添加此行 -->
  • 作用:所有 GET 请求的 URL 参数均使用 UTF-8 解码,一劳永逸解决乱码;
  • 适用场景:有权限修改服务器配置的环境(如开发、测试环境)。

方案二:使用请求体编码同步 URI 编码

若无法修改 Tomcat 配置,可在 Connector 中添加 useBodyEncodingForURI="true",使 URI 编码与请求体编码保持一致:

1
2
3
4
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
useBodyEncodingForURI="true" /> <!-- 添加此行 -->

同时,在 Servlet 中设置请求体编码(需在获取参数前调用):

1
2
3
4
5
6
7
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置请求体编码(会同步到 URI 编码)
request.setCharacterEncoding("UTF-8");
// 获取参数(此时已使用 UTF-8 解码)
String username = request.getParameter("username");
}
  • 注意useBodyEncodingForURI 仅在调用 request.setCharacterEncoding() 后生效,且优先级低于 URIEncoding

方案三:手动转码(不推荐,仅应急)

若以上配置均无法修改,可手动对乱码参数进行转码:

  1. 先用 ISO-8859-1 还原参数的字节数组;
  2. 再用 UTF-8 重新解码。
1
2
3
4
5
6
7
8
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
if (username != null) {
// 手动转码:ISO-8859-1 → 字节数组 → UTF-8
username = new String(username.getBytes("ISO-8859-1"), "UTF-8");
}
}
  • 缺点:每个参数都需手动处理,代码冗余,且易遗漏,仅建议作为临时解决方案。

POST 请求中文乱码解决方案

POST 请求参数放在请求体中,编码由 Content-Type: application/x-www-form-urlencoded;charset=UTF-8 中的 charset 决定,解决方案更直接:

核心方案:设置请求体编码

在 Servlet 的 doPost 方法中,获取参数前调用 request.setCharacterEncoding("UTF-8"),指定请求体的解码方式:

1
2
3
4
5
6
7
8
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 关键:设置请求体编码(必须在 getParameter() 前调用)
request.setCharacterEncoding("UTF-8");

// 获取参数(此时使用 UTF-8 解码)
String username = request.getParameter("username");
}
  • 原理:通知服务器用 UTF-8 解码请求体中的参数,与客户端表单提交的编码(通常为 UTF-8)匹配;
  • 注意:若客户端表单未指定 charset,需在 HTML 中设置表单编码:
1
2
3
4
5
<!-- 表单中指定编码(与服务器保持一致) -->
<form method="post" action="/user" accept-charset="UTF-8">
<input type="text" name="username">
<button type="submit">提交</button>
</form>

全局过滤方案:使用编码过滤器

为避免在每个 Servlet 中重复设置 request.setCharacterEncoding("UTF-8"),可通过过滤器(Filter) 统一处理所有 POST 请求:

(1)实现编码过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
@WebFilter("/*") // 拦截所有请求
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 设置请求编码(对 POST 有效,对 GET 需配合 Tomcat 配置)
request.setCharacterEncoding("UTF-8");
// 设置响应编码(避免响应中文乱码)
response.setContentType("text/html;charset=UTF-8");
// 继续传递请求
chain.doFilter(request, response);
}
}
(2)注册过滤器(若不使用注解)

web.xml 中配置过滤器:

1
2
3
4
5
6
7
8
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.example.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
  • 作用:全局统一设置请求 / 响应编码,简化代码,推荐在实际项目中使用。

响应中文乱码解决方案

若服务器返回的响应包含中文,需确保响应编码正确,否则客户端会乱码:

1
2
3
4
5
6
7
8
9
10
11
12
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 方式 1:设置响应类型和编码(推荐)
response.setContentType("text/html;charset=UTF-8");

// 方式 2:单独设置编码(需在 getWriter() 前调用)
// response.setCharacterEncoding("UTF-8");
// response.setHeader("Content-Type", "text/html");

// 输出中文
response.getWriter().write("你好,世界!");
}
  • 原理:通过 Content-Type 头通知客户端用 UTF-8 解码响应内容;
  • 注意setContentType() 需在 getWriter() 前调用,否则编码设置无效

欢迎关注我的其它发布渠道