Ajax 使用 XMLHttpRequest (或 ActiveXObject) POST 中文時,瀏覽器默認按頁面編碼 (如 UTF-8/GBK) 發送字節流。但早期 Java URLEncoder.encode 輸出 %XX%XX (雙字節拆分),而 JavaScript escape() 輸出 %uXXXX (Unicode 4位十六進制)。兩者解碼邏輯完全不同,導致服務端解碼亂碼。
var params = "chineseData=" + escape(document.getElementById("input").value);
xmlHttp.open("POST", "/test/zhongwen.jsp", false);
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlHttp.send(params);
提供 Escape 工具類,精確實現 JavaScript unescape 算法:
public class Escape {
private static final String[] HEX = {"00","01",...,"FF"}; // 256 條
private static final byte[] VAL = {0x3F, 0x3F, ..., 0x0F}; // 反查表
public static String unescape(String s) {
StringBuffer sbuf = new StringBuffer();
int i = 0, len = s.length();
while (i < len) {
char ch = s.charAt(i);
if (ch == '+') { sbuf.append(' '); }
else if (isUnreserved(ch)) { sbuf.append(ch); } // A-Z a-z 0-9 - _ . ! ~ * ' ( )
else if (ch == '%') {
if (i+1 < len && s.charAt(i+1) == 'u') { // %uXXXX Unicode
int code = (VAL[s.charAt(i+2)] << 12) | (VAL[s.charAt(i+3)] << 8)
| (VAL[s.charAt(i+4)] << 4) | VAL[s.charAt(i+5)];
sbuf.append((char) code);
i += 5;
} else { // %XX ASCII
int code = (VAL[s.charAt(i+1)] << 4) | VAL[s.charAt(i+2)];
sbuf.append((char) code);
i += 2;
}
}
i++;
}
return sbuf.toString();
}
// 配套 escape 方法(供 Java 客戶端調用 JS 服務時使用)
public static String escape(String s) { ... }
}
<%
String decoded = Escape.unescape(request.getParameter("chineseData"));
// 正常業務處理
%>
URLEncoder 輸出 %E4%B8%AD%EF%BF%BD (UTF-8 字節流編碼)escape() 輸出 %u4E2D%u6587 (Unicode 代碼點編碼)escape 直接取碼點,無歧義%uXXXX 解碼器,必須自行實現若項目可控制前後端,強烈建議棄用 escape 方案,改用標準化方案:
<meta charset="UTF-8">encodeURIComponent() (輸出 UTF-8 %XX%XX)URLDecoder.decode(param, "UTF-8")server.xml 配置 URIEncoding="UTF-8"request.setCharacterEncoding("UTF-8")此方案符合 RFC 3986,無需自定義工具類,支援所有語言字符,為現代 Web 應用首選。
歷史項目維護期,Escape 類完美解決 escape/unescape 互通問題;新項目請直接採用 UTF-8 + encodeURIComponent 全鏈路標準化方案,從源頭消除編碼地獄。