杰瑞科技汇

Servlet乱码Java如何解决?

乱码问题的根源

要解决乱码,首先要明白它为什么会发生,乱码的根本原因在于:编码和解码使用的字符集不一致

想象一下,你有一封用中文写的信(信息),你需要把它打包寄出去(编码/序列化),对方收到后需要拆开阅读(解码/反序列化)。

  • 编码:将字符(如 '中')转换成计算机可以存储和传输的字节流(如 [-28, -72, -83])。
  • 解码:将字节流([-28, -72, -83])重新还原成字符('中')。

如果在打包时你用的是 GBK 字符集,而对方拆包时误以为是 UTF-8 字符集,那么就会出现乱码,Servlet 乱码问题就发生在这个“打包”和“拆包”的过程中。

在 Web 开发中,主要涉及三个环节的编码:

  1. 浏览器端(客户端)编码:用户在浏览器中输入数据时,浏览器使用什么编码格式将数据发送给服务器。
  2. 服务器端(Tomcat)解码:Tomcat 接收到请求后,使用什么编码格式来解析请求数据(如 POST 请求体)。
  3. 服务器端(Tomcat)编码:Tomcat 将响应数据发送回浏览器前,使用什么编码格式将数据转换成字节流。
  4. 浏览器端(客户端)解码:浏览器接收到响应数据后,使用什么编码格式来解析并显示。

只要这四个环节中,任意一对“编码”和“解码”所用的字符集不一致,就可能产生乱码。


常见乱码场景及解决方案

我们根据不同的请求和响应类型来分别讨论解决方案。

GET 请求乱码

GET 请求的参数是附加在 URL 后面的,http://localhost:8080/test?name=张三&age=25

  • 乱码原因

    1. 浏览器将 张三 这个字符串,根据当前页面的编码(通常是 UTF-8)进行 URL 编码,变成 %E5%BC%A0%E4%B8%89
    2. 这个请求发送到 Tomcat 服务器,Tomcat 在接收到 URL 后,默认使用 ISO-8859-1 字符集来解码 URL 中的 %E5%BC%A0%E4%B8%89
    3. 由于 ISO-8859-1 字符集中不存在这些字节对应的中文,所以解码失败,变成了乱码。
  • 解决方案核心思路:将 Tomcat 默认的 ISO-8859-1 解码后的字节数组,再用正确的编码(UTF-8)重新编码一次。

    // 在 Servlet 的 doGet 或 doPost 方法中
    String name = request.getParameter("name");
    // 方案一:手动转换(适用于少量参数)
    // 1. 用 ISO-8859-1 解码成字节数组
    byte[] bytes = name.getBytes("ISO-8859-1");
    // 2. 用 UTF-8 重新编码成字符串
    name = new String(bytes, "UTF-8");
    // 方案二:一劳永逸的通用方法(推荐)
    // 写一个工具方法,在所有需要处理 GET 请求的地方调用
    request.setCharacterEncoding("UTF-8"); // 这对 GET 请求无效,但养成好习惯
    String name2 = new String(request.getParameter("name").getBytes("ISO-8859-1"), "UTF-8");
  • 更优的解决方案(修改 Tomcat 配置): 如果你的项目大量使用 GET 请求,每次手动转换很麻烦,可以修改 Tomcat 的配置文件,让它默认使用 UTF-8 解码 URL。

    1. 打开 Tomcat 安装目录下的 conf/server.xml 文件。
    2. 找到 <Connector> 标签,在其中添加 URIEncoding="UTF-8" 属性。
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               URIEncoding="UTF-8" /> <!-- 添加这一行 -->

    修改后重启 Tomcat,所有 GET 请求的乱码问题就都解决了。


POST 请求乱码

POST 请求的参数在请求体中,不是附加在 URL 后面。

  • 乱码原因

    1. 浏览器将表单数据用 UTF-8 编码后发送给 Tomcat。
    2. Tomcat 接收到数据后,默认使用 ISO-8859-1 字符集来解析请求体,导致乱码。
  • 解决方案核心思路:在获取请求参数之前,明确告诉 Tomcat 使用 UTF-8 字符集来解析请求体。

    // 在 Servlet 的 doGet 或 doPost 方法中,第一行代码就执行
    request.setCharacterEncoding("UTF-8");
    // 然后再获取参数
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    // ...

    注意request.setCharacterEncoding("UTF-8") 必须在 request.getParameter() 之前调用,否则无效。


响应乱码(服务器 -> 浏览器)

服务器处理完业务逻辑后,需要将数据(如 HTML 页面)返回给浏览器显示。

  • 乱码原因

    1. Tomcat 将你的响应内容(如 <h1>你好,世界</h1>)按照默认的 ISO-8859-1 字符集转换成字节流发送给浏览器。
    2. 浏览器接收到字节流后,根据页面声明的编码(或默认的编码)来解码,如果页面声明的是 UTF-8,而服务器发送的是 ISO-8859-1 编码的字节流,浏览器自然无法正确显示,导致乱码。
  • 解决方案核心思路:告诉 Tomcat 用 UTF-8 编码,同时告诉浏览器也用 UTF-8 解码。

    // 在 Servlet 的 doGet 或 doPost 方法中,第一行代码就执行
    response.setContentType("text/html;charset=UTF-8");
    // 然后获取输出流并写入内容
    PrintWriter out = response.getWriter();
    out.println("<h1>你好,世界!</h1>");
    out.println("<p>欢迎来到我的网站。</p>");

    response.setContentType("text/html;charset=UTF-8") 这行代码做了两件事:

    1. 设置响应内容的 MIME 类型为 text/html
    2. 设置响应内容的字符编码为 UTF-8,Tomcat 会据此将字符串转换成字节流。
    3. 它会向响应头中添加一个 Content-Type 头信息,Content-Type: text/html; charset=UTF-8,浏览器看到这个头信息,就知道应该用 UTF-8 来解码这个页面。

终极解决方案:统一编码过滤器

对于任何一个稍微复杂一点的 Web 应用,在每个 Servlet 的开头都写上两行编码设置代码是非常繁琐且容易出错的,我们使用 过滤器 来统一处理所有的请求和响应乱码问题。

这是一个标准且一劳永逸的做法。

创建一个编码过滤器 EncodingFilter

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// @WebFilter("/*") 表示拦截所有请求
@WebFilter("/*")
public class EncodingFilter implements Filter {
    private String encoding = "UTF-8";
    public void init(FilterConfig filterConfig) throws ServletException {
        // 可以从 web.xml 中读取配置的编码
        String encodingFromConfig = filterConfig.getInitParameter("encoding");
        if (encodingFromConfig != null) {
            this.encoding = encodingFromConfig;
        }
    }
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 1. 处理请求乱码
        request.setCharacterEncoding(encoding);
        // 2. 处理响应乱码
        response.setContentType("text/html;charset=" + encoding);
        // 3. 放行,让请求继续走到目标 Servlet
        chain.doFilter(request, response);
    }
    public void destroy() {
        // Filter 销毁时调用,一般无需处理
    }
}

配置 web.xml(可选,但推荐)

虽然我们用了 @WebFilter 注解,但为了更灵活地配置,也可以在 web.xml 中配置过滤器。

<filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>com.yourpackage.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

工作原理

  1. 当任何一个请求进入服务器时,首先会被 EncodingFilter 拦截。
  2. request.setCharacterEncoding("UTF-8") 会设置后续 request.getParameter() 解析 POST 请求体时使用的编码。
  3. response.setContentType("text/html;charset=UTF-8") 会设置 Tomcat 发送响应时使用的编码,并告诉浏览器用 UTF-8 解码。
  4. 对于 GET 请求,Tomcat 的 server.xml 中已经配置了 URIEncoding="UTF-8",那么就不会乱码,如果没有,过滤器无法直接修复 GET 请求乱码,强烈建议同时配置 Tomcat 的 URIEncoding

总结与最佳实践

场景 问题原因 解决方案 推荐指数
POST 请求 Tomcat 默认用 ISO-8859-1 解析请求体 request.setCharacterEncoding("UTF-8"); ⭐⭐⭐
响应乱码 Tomcat 默认用 ISO-8859-1 编码响应,浏览器可能用 UTF-8 解码 response.setContentType("text/html;charset=UTF-8"); ⭐⭐⭐
GET 请求 Tomcat 默认用 ISO-8859-1 解码 URL 修改 Tomcat server.xml (URIEncoding="UTF-8") ⭐⭐⭐⭐⭐
统一处理 手动在每个 Servlet 中设置编码太繁琐 使用 EncodingFilter 统一处理 ⭐⭐⭐⭐⭐

最佳实践组合拳:

  1. 修改 Tomcat 全局配置:在 conf/server.xml<Connector> 标签中添加 URIEncoding="UTF-8",这解决了所有 GET 请求的乱码问题。
  2. 创建并配置 EncodingFilter:创建一个 EncodingFilter,并配置拦截所有请求 (),这解决了所有 POST 请求和响应的乱码问题。

完成这两步后,你的整个 Web 应用就拥有了统一的、可靠的 UTF-8 编码环境,从此告别乱码烦恼。

分享:
扫描分享到社交APP
上一篇
下一篇