上篇介紹了「手動轉碼」與「`request.setCharacterEncoding()`」兩種臨時方案。生產環境需建立**全鏈路統一編碼體系**:請求過濾器、響應頭、JSP/模板引擎、數據庫連接、日誌、前端表單、JavaScript AJAX、文件上傳下載。本文提供生產級 `CharacterEncodingFilter` 實現、Spring Boot 自動配置、Tomcat/Jetty 容器配置、以及完整最佳實踐清單。
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);
}
}
<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>
@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
| 環節 | 配置要點 | 驗證方式 |
|---|---|---|
| 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` |
| 表單提交 | ` | 提交中文,後端接收正常 |
| AJAX / Fetch | JS 發送前 `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 ` | 日誌文件中文無亂碼 |
| 容器 URI 解碼 | Tomcat `server.xml` ` | GET 中文參數正常 |
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8"
useBodyEncodingForURI="true" />
<Set name="requestEncoding">UTF-8</Set>
<Set name="responseEncoding">UTF-8</Set>
server:
undertow:
default-encoding: UTF-8
亂碼本質是**編碼協議不一致**導致的字節流錯誤解碼。解決之道不是「轉碼技巧」,而是建立**全鏈路 UTF-8 強制統一體系**:請求過濾器兜底、容器 URI 解碼、響應頭聲明、模板編碼、數據庫連接、前端表單/AJAX、文件名編碼、日誌輸出。配合漸進式遷移與自動化驗證,可徹底根治亂碼顽疾。