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

SpringMVC 無法獲取請求參數問題排查與解決:%u 編碼兼容方案

     編輯:Java 綜合教學

SpringMVC 無法獲取請求參數問題排查與解決:%u 編碼兼容方案

使用框架雖能提升開發效率,但隱藏底層細節時,遇到問題往往無從下手。本文記錄一個典型案例:Android 客戶端發表中文評論失敗的排查全過程,供參考。

問題現象

原 .NET API 移植至 Java SpringMVC 後,Android APP 無法正常發表中文評論。Controller 以 @RequestParam 綁定 content 參數,卻始終為空:

@RequestMapping(value = "/Test.api")
public Object test(HttpServletRequest request, HttpServletResponse response,
    @RequestParam(value = "content", required = false, defaultValue="") String content) {
    // content 值為空
}

抓包分析

用 Charles 抓包發現:Form Data 中確有 content 欄位且含中文,但 Raw Data 卻是 content=%u611f%u53d7%u4e00%u4e0b%u8d85%u4eba%u7684%u808c%u8089%uff0c。標準 URLEncoder 將漢字編碼為 %xy(兩位十六進位),而此處為 %u 開頭後接四位十六進位,屬 Unicode 編碼格式。Charles 可自動解碼顯示中文,但 SpringMVC 統一指定 UTF-8 解碼,導致異常而丟棄參數。

根因確認

Android 團隊確認過去使用自定義 UrlEncodeUnicode 方法編碼 POST 數據:

public static String UrlEncodeUnicode(final String s) {
    // ... 逐字元處理,ASCII 走標準 %xy,非 ASCII 走 %uXXXX
}

棄用計畫已定,但新版 API 需兼容新舊兩種編碼。

解決方案探討

@RequestParam 在框架層完成解碼,需在更早層面攔截原始數據、判斷編碼、動態解碼。經 DEBUG 確認兩條可行路徑:

方案一:改用 @RequestBody 獲取 Raw Data,Controller 層自行解析解碼

@RequestMapping(value = "/Test.api")
public Object test(HttpServletRequest request, HttpServletResponse response, @RequestBody String body) {
    // 獲得 raw 數據:Id=185904&content=%u611f%u53d7%u4e00%u4e0b
    // 自行解析、判斷編碼、解碼
}

優點:不需修改 web.xml,改動最小;缺點:需自行實現表單解析邏輯。

方案二:自定義 Filter,在 doFilter() 中讀取 getInputStream() 獲取 Raw Data,解析後 setAttribute 回 request

public class MyFilter implements Filter {
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) 
            throws IOException, ServletException {
        InputStream stream = servletRequest.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
        StringBuilder body = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            body.append(line);
        }
        // 解析 body,判斷編碼,解碼,將參數 setAttribute 回 request
        // 注意:getInputStream() 只能讀取一次!後續 getParameterMap() 等將失效
        chain.doFilter(request, response);
    }
}

需在 web.xml 配置 Filter,且 getInputStream() 為一次性操作,若 Filter 中讀取,後續 Controller 將無法再獲取參數,必須在 Filter 中完成所有參數解析並存回 request 屬性。

關鍵注意事項

  • ServletRequest.getInputStream() 僅能調用一次,讀取後流關閉,後續任何嘗試獲取參數(getParameter、getParameterMap)均會失效。
  • 方案一較輕量,推薦優先採用;方案二侵入性較大,僅在需全局統一處理時考慮。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved