| 特性 | forward (請求轉發) | redirect (重導向) |
|---|---|---|
| 機制 | 伺服器端內部轉發 | 瀏覽器端發起新請求 |
| URL 變化 | 不變 | 變為目標 URL |
| 請求物件 | 共享同一 request | 新 request,原 request 失效 |
| 資料傳遞 | request.setAttribute / session / 參數 | session / URL 參數 |
| 目標範圍 | 同一 Web 應用內部 | 任意 URL (含外部網站) |
| 速度 | 較快 (一次請求) | 較慢 (兩次請求) |
| 刷新行為 | 重複提交原表單 | 重新請求目標頁面 |
// 方式一:透過 request
RequestDispatcher rd = request.getRequestDispatcher("/result.jsp");
rd.forward(request, response);
// 方式二:透過 ServletContext (跨應用需 crossContext=true)
getServletContext().getRequestDispatcher("/otherApp/page.jsp").forward(request, response);
// 相對路徑 (相對當前應用 context path)
response.sendRedirect("success.jsp");
// 絕對路徑 (相對網域根目錄)
response.sendRedirect("/myapp/dashboard.jsp");
// 外部網站
response.sendRedirect("https://www.example.com");
<jsp:forward> 標籤
<jsp:forward page="/result.jsp">
<jsp:param name="msg" value="操作成功" />
</jsp:forward>
response.sendRedirect() 在 JSP 中的陷阱
<%
// 錯誤:out.flush() 後不能 redirect
out.println("處理中...");
out.flush(); // 已提交回應
response.sendRedirect("ok.jsp"); // 拋出 IllegalStateException
%>
解決:邏輯判斷前置,或使用 <c:redirect> (JSTL),或改用 forward。
response.setHeader("Location", url)
<%
response.setHeader("Location", "login.jsp");
response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); // 302
%>
等同於 sendRedirect,但需手動設定狀態碼,不建議直接使用。
// Servlet
User user = userService.findById(id);
request.setAttribute("user", user);
request.getRequestDispatcher("/user/detail.jsp").forward(request, response);
// JSP (EL 表達式)
${user.username} - ${user.email}
// POST 處理完成後 redirect 到 GET 頁面,避免重複提交
@WebServlet("/user/save")
public class UserSaveServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
User user = parseUser(req);
userService.save(user);
// 方式一:session 暫存 (適合成功訊息)
req.getSession().setAttribute("flashMsg", "儲存成功");
// 方式二:URL 參數 (適合 ID 導向)
resp.sendRedirect(req.getContextPath() + "/user/detail?id=" + user.getId());
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
IllegalStateException: Cannot forward after response has been committed | 已有輸出內容 (out.flush/println) 後嘗試 forward | 調整邏輯順序,前置判斷;或增加緩衝區大小 response.setBufferSize() |
| forward 後表單重複提交 | 瀏覽器重新整理重送 POST | 採用 PRG 模式:POST 處理完 redirect 到 GET 頁面 |
| redirect 丟失 request 屬性 | redirect 產生新 request | 改用 session、URL 參數、或 Spring FlashMap |
| 跨應用 forward 失效 | 預設禁止跨應用轉發 | Tomcat context.xml 設定 crossContext="true" |
forward 是伺服器端內部協作,redirect 是瀏覽器端雙向互動。現代 Web 開發核心原則:POST 請求必須 redirect (PRG 模式),GET 請求視情境選擇。善用 request.setAttribute 配合 forward 傳遞模型資料,用 session/URL 參數配合 redirect 傳遞狀態資訊,構建清晰、安全、用戶體驗良好的 Web 應用。