`RequestDispatcher.forward()` (伺服器端轉發) 與 `HttpServletResponse.sendRedirect()` (客戶端重定向) 是 Servlet 開發最易混淆的兩大跳轉方式。本文從請求對象共享、地址欄變化、執行位置、跨應用能力、參數傳遞、緩衝區限制**六大維度深度對比。
| 維度 | forward (轉發) | sendRedirect (重定向) |
|---|---|---|
| 執行位置 | 伺服器內部 (容器) | 客戶端瀏覽器 |
| 請求對象 | 同一個 request 共享屬性 | 新建 request,舊屬性丟失 |
| 瀏覽器地址欄 | 不變 (仍為原 URL) | 改變 為目標 URL |
| HTTP 狀態碼 | 200 (單次請求) | 302 (兩次請求:302 → 新請求) |
| 跨 Web 應用 | 需 `ServletContext.getContext(uri).getRequestDispatcher()` | 支援任意 URL (含外部域名) |
| 參數傳遞 | 只能 `request.setAttribute()` | URL 參數 `?id=1` 或 Session |
| 目標資源類型 | 同應用內 Servlet/JSP/HTML | 任意資源 (HTML、JSP、Servlet、外部站點) |
| Frame/Iframe 相容 | 不支援含 frame 的 JSP (部分容器) | 完全支援 |
| 緩衝區限制 | 響應未提交前可調用 (`isCommitted()`) | 隨時可調用 (發送 302 即提交) |
| 刷新行為 | 重複提交風險 (重新執行轉發邏輯) | 重複最後一個 GET 請求 (相對安全) |
// ========== forward ==========
// Servlet A
request.setAttribute("user", userObj);
request.getRequestDispatcher("/result.jsp").forward(request, response);
// result.jsp 可直接 ${user} 取值,地址欄仍為 /servletA
// ========== sendRedirect ==========
// Servlet B
response.sendRedirect("/result.jsp?userId=" + user.getId());
// result.jsp 需 request.getParameter("userId") 重新查詢,地址欄變為 /result.jsp
| 場景 | forward 路徑 | sendRedirect 路徑 |
|---|---|---|
| 應用上下文 `/myapp` | 相對當前請求路徑 `rd.forward("/b")` → `/myapp/b` `getServletContext().getRD("/b")` → `/myapp/b` | 相對伺服器根 `resp.sendRedirect("/b")` → `https://host/b` `resp.sendRedirect("b")` → `https://host/myapp/current/path/b` |
| 跨應用 | `ctx = getServletContext().getContext("/otherApp"); ctx.getRD("/b").forward(req,resp);` | `resp.sendRedirect("https://host/otherApp/b");` |
<jsp:forward page="/result.jsp" /> <!-- 等同 request.getRD().forward() -->
<c:redirect url="/result.jsp" /> <!-- JSTL 封裝 sendRedirect -->
<meta http-equiv="refresh" content="0;url=/result.jsp" /> <!-- HTML Meta 刷新 -->
精準掌握轉發與重定向語義邊界,是 Servlet/JSP/Web 框架開發者的必修基本功。