首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2EE

Java Web 亂碼終極解決方案(下):統一字符編碼過濾器與最佳實踐清單

     編輯:J2EE

前言:從症狀治療到體系治理

上篇介紹了「手動轉碼」與「`request.setCharacterEncoding()`」兩種臨時方案。生產環境需建立**全鏈路統一編碼體系**:請求過濾器、響應頭、JSP/模板引擎、數據庫連接、日誌、前端表單、JavaScript AJAX、文件上傳下載。本文提供生產級 `CharacterEncodingFilter` 實現、Spring Boot 自動配置、Tomcat/Jetty 容器配置、以及完整最佳實踐清單。

1. 請求編碼過濾器:核心實現

1.1 標準 Servlet Filter (通用)

public class CharacterEncodingFilter implements Filter {
    private String encoding = "UTF-8";
    private boolean forceRequestEncoding = true;
    private boolean forceResponseEncoding = true;

    @Override
    public void init(FilterConfig config) {
        String enc = config.getInitParameter("encoding");
        if (enc != null) encoding = enc;
        forceRequestEncoding = Boolean.parseBoolean(config.getInitParameter("forceRequestEncoding"));
        forceResponseEncoding = Boolean.parseBoolean(config.getInitParameter("forceResponseEncoding"));
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        if (forceRequestEncoding || request.getCharacterEncoding() == null) {
            request.setCharacterEncoding(encoding);
        }
        if (forceResponseEncoding) {
            response.setCharacterEncoding(encoding);
            response.setContentType("text/html;charset=" + encoding);
        }
        chain.doFilter(request, response);
    }
}

1.2 web.xml 註冊 (務必放在所有 Filter 之前)

<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>com.demo.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>  <!-- 關鍵:轉發也需過濾 -->
</filter-mapping>

2. Spring Boot 自動配置 (零 XML)

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
        FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new CharacterEncodingFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 最優先
        return bean;
    }
}

或直接在 `application.yml`:

server:
  servlet:
    encoding:
      charset: UTF-8
      force: true
      force-request: true
      force-response: true

3. 全鏈路編碼一致性清單

環節配置要點驗證方式
HTTP 請求Filter `request.setCharacterEncoding("UTF-8")`、`forceRequestEncoding=true`Controller 打印 `request.getCharacterEncoding()`
HTTP 響應Filter `response.setCharacterEncoding("UTF-8")`、`setContentType("text/html;charset=UTF-8")`瀏覽器 DevTools → Response Headers → Content-Type
JSP 頁面`<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>`瀏覽器查看源碼 → 中文正常
Thymeleaf/FreeMarker`spring.thymeleaf.encoding=UTF-8` / `spring.freemarker.charset=UTF-8`模板輸出中文正常
數據庫連接JDBC URL `characterEncoding=utf8&useUnicode=true` (MySQL)
`?charSet=UTF-8` (PostgreSQL)
DB 客戶端查看表數據、表結構 `CHARSET=utf8mb4`
表單提交`
` (HTML5)
提交中文,後端接收正常
AJAX / FetchJS 發送前 `encodeURIComponent()`,Header `Content-Type: application/json;charset=UTF-8`Network 面板查看 Payload/Headers
文件上傳Multipart Filter 需在 Encoding Filter 之後,或用 Spring `StandardServletMultipartResolver`上傳中文文件名、內容正常
文件下載`response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + URLEncoder.encode(name, "UTF-8"))`瀏覽器下載文件名正常
日誌輸出Logback/Log4j2 `UTF-8`、控制台/文件編碼一致日誌文件中文無亂碼
容器 URI 解碼Tomcat `server.xml` `` (GET 參數、路徑參數)GET 中文參數正常

4. 容器級配置:Tomcat / Jetty / Undertow

Tomcat (server.xml)

<Connector port="8080" protocol="HTTP/1.1"
           URIEncoding="UTF-8"
           useBodyEncodingForURI="true" />

Jetty (jetty-http.xml)

<Set name="requestEncoding">UTF-8</Set>
<Set name="responseEncoding">UTF-8</Set>

Undertow (Spring Boot embedded)

server:
  undertow:
    default-encoding: UTF-8

5. 常見誤區與避坑

  1. 誤區:`request.setCharacterEncoding()` 可多次調用。
    真相:只能調用一次 (讀取 body 前),多次調用拋 `IllegalStateException`。Filter 中 `force=true` 確保首次生效。
  2. 誤區:GET 參數由 Filter 處理。
    真相:GET 參數在 `request.getParameter()` 前由容器按 `URIEncoding` 解碼,**Filter 無效**。必須配置容器 `URIEncoding="UTF-8"`。
  3. 誤區:`ISO-8859-1` 是「萬能中轉」。
    真相:僅適用於「錯誤以 ISO-8859-1 解碼的字節流」修正,正確鏈路應全程 UTF-8,避免雙重轉碼損耗與風險。
  4. 誤區:`pageEncoding` 與 `contentType` 只需設一個。
    真相:`pageEncoding` 決定 JSP 源文件讀取編碼,`contentType` 決定響應頭 charset,**兩者必須一致且同時設置**。

6. 遷移存量項目:漸進式策略

  1. 新增 `CharacterEncodingFilter` (force=false) 觀察日誌,確認無副作用。
  2. 逐步開啟 `forceRequestEncoding=true`,重點測試 POST 表單、AJAX、文件上傳。
  3. 統一數據庫連接編碼,驗證存量數據讀寫 (注意 `utf8` vs `utf8mb4`)。
  4. 修正所有 JSP/模板頁面編碼聲明。
  5. 全量開啟 `forceResponseEncoding=true`,配置容器 `URIEncoding`。
  6. 全鏈路壓測、滾動發布、監控錯誤率。

結語

亂碼本質是**編碼協議不一致**導致的字節流錯誤解碼。解決之道不是「轉碼技巧」,而是建立**全鏈路 UTF-8 強制統一體系**:請求過濾器兜底、容器 URI 解碼、響應頭聲明、模板編碼、數據庫連接、前端表單/AJAX、文件名編碼、日誌輸出。配合漸進式遷移與自動化驗證,可徹底根治亂碼顽疾。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved