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

利用過濾器與裝飾者模式(靜態代理)解決 GET 請求中文亂碼問題

     編輯:Java 綜合教學

前言

在 Java Web 開發中,POST 請求的中文亂碼只需在 `ServletRequest` 上設定字元編碼即可輕鬆解決。然而,GET 請求的參數編碼處理卻較為棘手,因為參數附在 URL 上,伺服器預設使用 ISO-8859-1 解碼,導致中文出現亂碼。本文將介紹如何結合過濾器與裝飾者設計模式(靜態代理),優雅地增強 `HttpServletRequest` 的 `getParameter` 方法,從根源上解決 GET 請求亂碼問題。

核心思路:增強 Request 對象

解決方案的核心在於攔截請求並包裝原始 Request。我們建立一個繼承自 `HttpServletRequestWrapper` 的包裝類,重寫 `getParameter` 方法:當檢測到是 GET 請求時,手動將參數從 ISO-8859-1 轉碼為 UTF-8;其他情況則直接委託給原始請求對象。最後在過濾器中用這個包裝類替換原始 request 繼續傳遞給後續過濾器或 Servlet。

為何選擇裝飾者模式(靜態代理)?

面對增強介面實現類的需求,常見方案有繼承、裝飾者模式、動態代理、字節碼增強等。

  • 繼承:需知道具體實現類全名(如 Tomcat 的 `RequestFacade`),且難以跨容器通用,佔用寶貴的 `extends` 名額,擴展性極差。
  • 裝飾者模式(靜態代理):優點在於面向介面編程,不依賴具體實現類,不佔用繼承名額,代碼擴展性極強。缺點是若介面方法過多,需實現所有無關方法,但可繼承 `HttpServletRequestWrapper` 解決此問題。
  • 動態代理 / 字節碼增強:功能更強,但複雜度較高,屬進階主題。

綜合考量,裝飾者模式配合 `HttpServletRequestWrapper` 是解決此問題的最佳實踐。

程式碼實作

1. 定義請求包裝類

繼承 `HttpServletRequestWrapper`,僅重寫 `getParameter` 方法:

package com.example.utils;

import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class EncodingRequestWrapper extends HttpServletRequestWrapper {

    public EncodingRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getParameter(String name) {
        // 僅處理 GET 請求
        if ("get".equalsIgnoreCase(super.getMethod())) {
            String value = super.getParameter(name);
            if (value != null) {
                try {
                    // 核心轉碼邏輯:ISO-8859-1 -> UTF-8
                    return new String(value.getBytes("ISO-8859-1"), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException("編碼轉換失敗", e);
                }
            }
        }
        // 非 GET 或參數為 null,直接返回原始值
        return super.getParameter(name);
    }
}

2. 編寫字元編碼過濾器

在 `doFilter` 中設定 POST 編碼,並用包裝類替換 request:

import java.io.IOException;
import javax.servlet.*;

public class CharacterEncodingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        // 1. 解決 POST 亂碼
        request.setCharacterEncoding("UTF-8");
        
        // 2. 解決 GET 亂碼:包裝請求對象
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        EncodingRequestWrapper wrappedRequest = new EncodingRequestWrapper(httpRequest);
        
        // 3. 傳遞包裝後的請求
        chain.doFilter(wrappedRequest, response);
    }
    
    // init 和 destroy 方法省略...
}

3. 在 web.xml 中註冊過濾器

<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>com.example.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

原理解析

  1. 過濾器攔截:所有請求進入應用的第一關。
  2. POST 處理:`request.setCharacterEncoding("UTF-8")` 設定請求體解碼格式,必須在讀取參數前調用。
  3. GET 處理:Tomcat 等容器預設用 ISO-8859-1 解碼 URL 參數。包裝類在 `getParameter` 內部將取得的字串按 ISO-8859-1 還原為位元組陣列,再按 UTF-8 重組字串,實現透明轉碼。
  4. 裝飾者模式體現:`EncodingRequestWrapper` 實現 `HttpServletRequest` 介面(透過繼承 Wrapper),持有原始 request 引用,對外暴露相同介面,但在特定方法上增加增強邏輯,完美符合裝飾者模式定義。

注意事項與最佳實踐

  • 過濾器順序:字元編碼過濾器應盡可能靠前配置,確保在其他過濾器或 Servlet 讀取參數前生效。
  • Tomcat 8+ 配置:新版 Tomcat 可在 `server.xml` 的 `` 標籤加上 `URIEncoding="UTF-8"`,從容器層面解決 GET 亂碼,無需代碼干預。但理解代碼層面的解決方案對於排查問題、適配老舊環境仍有價值。
  • 編碼一致性:確保頁面、資料庫、JVM、容器編碼全鏈路統一為 UTF-8。

總結

結合過濾器進行統一攔截,利用裝飾者模式(靜態代理)包裝 `HttpServletRequest` 並重寫 `getParameter`,是解決 GET 請求中文亂碼的經典、通用且優雅的方案。它體現了面向介面編程、單一職責與開閉原則,是 Java Web 基礎架構中值得掌握的設計模式實戰案例。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved