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

Java GET 請求中文亂碼終極解決方案:三種編碼策略原理對比與最佳實踐

     編輯:Java 綜合教學

前言

GET 請求中文亂碼是 Java Web 開發經典痛點。根因在於 瀏覽器 URL 編碼行為與服務端解碼行為不一致。本文系統講解三種解決方案的原理、適用場景、優缺點,並給出生產環境推薦配置。

一、問題根因還原

流程圖譜

瀏覽器輸入中文 
  → 瀏覽器 URL 編碼 (UTF-8/GBK 取決於頁面 charset) 
  → HTTP 請求行: GET /api?name=%E4%B8%AD%E6%96%87 HTTP/1.1
  → Tomcat 接收原始位元組
  → Tomcat 解碼 URL (默認 ISO-8859-1) → 字符串 "中文"
  → Servlet 獲取參數 request.getParameter("name") → 亂碼字符串

關鍵點

  • HTTP 協議規定 URL 僅允許 ASCII,中文必須編碼為 %XX%XX 形式。
  • Tomcat 默認用 ISO-8859-1 解碼 URL (Servlet 規範歷史包袱)。
  • POST 請求體編碼由 Content-Type: charset=... 指定,默認 UTF-8,通常無亂碼。

二、三大解決方案深度對比

方案一:手動重新編碼 (代碼層修復)

// Controller 中
String param = request.getParameter("name"); // 獲取 ISO-8859-1 解碼後的錯誤字符串
String correct = new String(param.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
優點缺點適用場景
無需修改服務器配置,部署無關侵入業務代碼,易漏寫,需封裝 Filter/Interceptor 統一處理無法控制服務器配置的共享主機、舊項目快速修復

方案二:配置 Tomcat URIEncoding (服務器層修復)

<!-- server.xml -->
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           URIEncoding="UTF-8" />
<!-- 或 useBodyEncodingForURI="true" 配合 request.setCharacterEncoding -->
優點缺點適用場景
一勞永逸,代碼零侵入強依賴 Tomcat,遷移 Jetty/Undertow 需重新配置;Docker/K8s 需構建鏡像注入配置自建服務器、傳統 WAR 部署、團隊統一 Tomcat 版本

方案三:雙重 URL 編碼 + 服務端單次解碼 (協議層修復,推薦)

核心原理

  1. 前端 JS 對參數進行兩次 encodeURIComponent (UTF-8) → `%E4%B8%AD` → `%25E4%25B8%25AD`
  2. 發送請求,Tomcat 自動進行一次 URL 解碼 (ISO-8859-1) → 得到 `%E4%B8%AD` (UTF-8 編碼形式)
  3. 後端手動進行一次 URL 解碼 (UTF-8) → 還原中文

前端代碼 (Vue/React/jQuery 通用)

// 工具函數
function doubleEncode(str) {
    return encodeURIComponent(encodeURIComponent(str));
}

// 使用
const params = {
    name: doubleEncode("中文"),
    keyword: doubleEncode("搜索內容")
};
axios.get('/api/search', { params });
// 請求 URL: /api/search?name=%25E4%25B8%25AD%25E6%2596%2587&keyword=...

後端統一解碼 Filter (Spring Boot)

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DoubleEncodeDecodeFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        // 包裝請求,覆寫 getParameter 系列方法
        chain.doFilter(new DoubleEncodeRequestWrapper(request), res);
    }
}

class DoubleEncodeRequestWrapper extends HttpServletRequestWrapper {
    public DoubleEncodeRequestWrapper(HttpServletRequest request) { super(request); }
    
    private String decode(String value) {
        if (value == null) return null;
        try {
            // Tomcat 已解碼一次 (ISO-8859-1),我們再解碼一次 (UTF-8)
            return URLDecoder.decode(value, StandardCharsets.UTF_8.name());
        } catch (Exception e) {
            return value; // 解碼失敗返回原值
        }
    }
    
    @Override public String getParameter(String name) {
        return decode(super.getParameter(name));
    }
    @Override public Map<String, String[]> getParameterMap() {
        Map<String, String[]> map = super.getParameterMap();
        map.replaceAll((k, v) -> Arrays.stream(v).map(this::decode).toArray(String[]::new));
        return map;
    }
    @Override public String[] getParameterValues(String name) {
        String[] vals = super.getParameterValues(name);
        return vals == null ? null : Arrays.stream(vals).map(this::decode).toArray(String[]::new);
    }
}
優點缺點適用場景
完全符合 HTTP 協議,不依賴特定容器配置,遷移零成本,安全性高 (雙重編碼防 XSS)前後端需協同修改,舊接口兼容需雙版本微服務、雲原生、多容器環境、前後端分離項目 (強烈推薦)

三、Spring Boot 2.x/3.x 統一配置最佳實踐

1. 全局強制 UTF-8 (application.yml)

server:
  servlet:
    encoding:
      charset: UTF-8
      force: true
      enabled: true
  # Tomcat 專屬 (Spring Boot 內嵌 Tomcat 自動生效)
  tomcat:
    uri-encoding: UTF-8
    # 兼容雙重編碼場景:若前端已雙重編碼,此處可關閉或保留
    # use-body-encoding-for-uri: false

2. 過濾器鏈順序關鍵

@Bean
public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
    CharacterEncodingFilter filter = new CharacterEncodingFilter();
    filter.setEncoding("UTF-8");
    filter.setForceEncoding(true);
    FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>(filter);
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 必須最早執行
    return bean;
}

// 若使用雙重編碼方案,DoubleEncodeDecodeFilter 順序需在 CharacterEncodingFilter 之後
// 因為前者依賴 Tomcat 已完成第一次解碼

四、POST 請求亂碼補充

POST 表單/JSON 亂碼通常由 request.setCharacterEncoding("UTF-8") 未在讀取流前調用導致。Spring Boot CharacterEncodingFilter 已自動處理。**文件上傳需配置 `StandardServletMultipartResolver` 或 Commons FileUpload 設定編碼。**

五、測試驗證清單

  • [ ] 瀏覽器 Network 面板檢查 Request URL 中文是否為 `%E4%B8%AD%E6%96%87` (單次編碼) 或 `%25E4%25B8%25AD` (雙重編碼)
  • [ ] 後端斷點檢查 `request.getParameter("name")` 返回值
  • [ ] 特殊字符測試:`%`、`&`、`=`、`+`、空格、Emoji 😀
  • [ ] 多容器驗證:Tomcat、Jetty、Undertow、Docker、K8s Ingress

結語

GET 亂碼本質是編碼協議不匹配。**雙重 URL 編碼方案**通過前後端協同,將編碼/解碼責任納入應用層,擺脫容器配置依賴,是微服務雲原生時代的最優解。建議新項目啟動即確立此規範,舊項目通過 Filter 兼容遷移。徹底告別亂碼,從理解協議開始。

  1. 上一篇文章:
  2. 下一篇文章:
Java IO 流技術體系全景解析:字節流、字符流、處理流與序列化實戰 JS 載入時機陷阱:為什麼