計算機只識別 0/1。字符 → 位元組叫編碼,位元組 → 字符叫解碼。編碼集 ≠ 解碼集 即產生亂碼。Java Web 涉及四個關鍵環節:
瀏覽器 --(HTTP Request)--> Tomcat --(Servlet API)--> Java Code
│ │ │
編碼(UTF-8) 解碼(ISO-8859-1?) 處理
│ │ │
◄--(HTTP Response)--- Tomcat --(Servlet API)-- 返回
解碼(UTF-8?) 編碼(ISO-8859-1?)
<!-- HTML5 簡潔寫法,必須放在 首行 -->
<meta charset="UTF-8">
<!-- 表單提交強制指定編碼 (GET 方式無效) -->
<form action="/submit" method="post" accept-charset="UTF-8">
<!-- server.xml 節點 -->
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"
useBodyEncodingForURI="true" />
URIEncoding="UTF-8":解決 GET 請求 URL 參數中文亂碼 (Tomcat 8+ 默認已是 UTF-8)useBodyEncodingForURI="true":讓 POST 請求體編碼也受 request.setCharacterEncoding 影響@WebServlet("/submit")
public class SubmitServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 關鍵:必須在讀取任何參數前調用,且僅對 POST 有效
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username"); // 正確解碼
// 業務邏輯...
}
}
原理:POST 數據在 Request Body,Tomcat 默認用 ISO-8859-1 解碼。調用 setCharacterEncoding 會重新用指定字符集解析 Body。
GET 參數在 URL,setCharacterEncoding 無效。兩種方案:
new String(req.getParameter("name").getBytes("ISO-8859-1"), "UTF-8")CharacterEncodingFilter):
@WebFilter("/*")
public class EncodingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 1. 請求編碼 (POST 自動生效,GET 需手動處理 URL)
request.setCharacterEncoding("UTF-8");
// 2. GET 參數手動轉碼包裝
if ("GET".equalsIgnoreCase(request.getMethod())) {
request = new HttpServletRequestWrapper(request) {
@Override public String getParameter(String name) {
String val = super.getParameter(name);
if (val == null) return null;
try { return new String(val.getBytes("ISO-8859-1"), "UTF-8"); }
catch (UnsupportedEncodingException e) { return val; }
}
@Override public String[] getParameterValues(String name) {
String[] vals = super.getParameterValues(name);
if (vals == null) return null;
return Arrays.stream(vals)
.map(v -> {
try { return new String(v.getBytes("ISO-8859-1"), "UTF-8"); }
catch (UnsupportedEncodingException e) { return v; }
}).toArray(String[]::new);
}
};
}
// 3. 響應編碼
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
chain.doFilter(request, response);
}
}
// 方式一:雙重保險 (推薦)
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8"); // 告訴瀏覽器以 UTF-8 解碼
// 方式二:一行搞定 (等效)
resp.setContentType("text/html;charset=UTF-8"); // 內部會調用 setCharacterEncoding
// JSON 接口
resp.setContentType("application/json;charset=UTF-8");
# application.yml
server:
servlet:
encoding:
charset: UTF-8
force: true # 強制請求/響應編碼
enabled: true
# 等同於自動註冊 CharacterEncodingFilter(forceEncoding=true)
| 現象 | 原因 | 修正 |
|---|---|---|
| POST 中文亂碼 | 未調用 request.setCharacterEncoding("UTF-8") | Filter 或 Servlet 首行設置 |
| GET 中文亂碼 | Tomcat 默認 ISO-8859-1 解碼 URL | server.xml URIEncoding="UTF-8" + Filter 包裝 |
| 響應頁面亂碼 | 未設置 Content-Type charset | resp.setContentType("text/html;charset=UTF-8") |
| 文件下載中文名亂碼 | Header 編碼不符合 RFC 5987 | URLEncoder.encode(name, "UTF-8").replace("+", "%20") + filename*=UTF-8'' |
| 數據庫存儲亂碼 | 連接串無字符集 / 表字符集非 utf8mb4 | JDBC URL ?characterEncoding=utf8&useUnicode=true,表 CHARSET=utf8mb4 |
中文亂碼本質是 編碼/解碼鏈路任意一環不一致。解決之道:全鏈路 UTF-8 —— 頁面 <meta charset="UTF-8">、Tomcat URIEncoding="UTF-8"、Filter 統一 setCharacterEncoding("UTF-8")、響應 setContentType(...;charset=UTF-8)、數據庫 utf8mb4。Spring Boot 僅需 server.servlet.encoding.force=true 一行配置。