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

Servlet 請求轉發亂碼根因:response.getWriter() 鎖定 ISO-8859-1 導致後續 setCharacterEncoding 無效

     編輯:Java 綜合教學

前言

Servlet 請求轉發(RequestDispatcher.forward())時出現亂碼,且單獨訪問目標頁面正常。本文揭示核心原因:首次調用 response.getWriter() 會鎖定響應字符編碼為 ISO-8859-1,後續 setCharacterEncoding 失效,並給出三種標準解決方案。

一、現象復現

@WebServlet("/forward")
public class ForwardServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        
        // ❌ 錯誤順序:先獲取 Writer,再設置編碼
        PrintWriter out = resp.getWriter(); // 此處鎖定編碼為 ISO-8859-1
        resp.setCharacterEncoding("UTF-8"); // 無效!
        resp.setContentType("text/html;charset=utf-8"); // 無效!
        
        RequestDispatcher rd = req.getRequestDispatcher("/target.jsp");
        rd.forward(req, resp); // target.jsp 顯示亂碼
    }
}

二、根因深度解析

源碼證據

HttpServletResponseWrapper.getWriter() 內部調用:

public PrintWriter getWriter() throws IOException {
    if (writer != null) return writer;
    // 關鍵:若未顯式設置 charset,默認 ISO-8859-1
    String charset = getCharacterEncoding(); 
    if (charset == null) charset = "ISO-8859-1"; // Servlet 規範默認值
    writer = new PrintWriter(new OutputStreamWriter(getOutputStream(), charset));
    return writer;
}

鎖定機制

  1. 調用 getWriter() → 創建 OutputStreamWriter(..., "ISO-8859-1")
  2. 返回的 PrintWriter 綁定該編碼器
  3. 後續 setCharacterEncoding("UTF-8") 僅修改響應頭 Content-Type,不會重新創建 Writer
  4. 轉發時目標頁面複用同一 response 對象,Writer 已鎖定 → 輸出亂碼

三、三種標準解決方案

方案一:先設置編碼,再獲取 Writer(最簡單)

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException {
    
    // ✅ 正確順序:先編碼,後 getWriter
    resp.setCharacterEncoding("UTF-8");
    resp.setContentType("text/html;charset=UTF-8");
    
    PrintWriter out = resp.getWriter(); // 此時綁定 UTF-8
    RequestDispatcher rd = req.getRequestDispatcher("/target.jsp");
    rd.forward(req, resp);
}

方案二:使用 Filter 統一設置(推薦生產)

@WebFilter("/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
            throws IOException, ServletException {
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        req.setCharacterEncoding("UTF-8"); // 兼容 POST 請求體
        chain.doFilter(req, resp);
    }
}

方案三:目標頁面頁面指令聲明(JSP)

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>

但轉發前的 Servlet 必須不調用 getWriter(),否則仍會鎖定編碼。

四、GET vs POST 亂碼差異補充

場景原因解決
POST 請求體亂碼Tomcat 默認 ISO-8859-1 解碼 Bodyreq.setCharacterEncoding("UTF-8") 在 getParameter() 前調用
GET 參數亂碼Tomcat 8+ 默認 UTF-8,舊版需配置 URIEncoding="UTF-8"server.xml Connector 加 URIEncoding="UTF-8"
轉發亂碼getWriter() 鎖定編碼本文三種方案

五、最佳實踐清單

  1. Filter 統一編碼:專案啟動即配置 EncodingFilter,覆蓋所有請求
  2. Servlet 禁用 getWriter/getOutputStream:轉發場景下絕不**在 forward 前調用
  3. JSP 頁面指令雙保險:contentType + pageEncoding 同時聲明
  4. Tomcat 配置 URIEncoding:<Connector URIEncoding="UTF-8" /> 解決 GET 參數
  5. 資源文件統一 UTF-8:IDE、Maven、資料庫連接串 characterEncoding=utf8

總結

Servlet 轉發亂碼的核心是 response.getWriter() 過早調用鎖定了 ISO-8859-1 編碼。鐵律:**響應編碼設置必須在獲取輸出流之前完成**。生產環境請統一使用 Filter 方案,從架構層面消滅編碼問題。

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