「轉發」與「重定向」是 Web 開發中最基礎也最易混淆的兩種頁面跳轉方式。本文從 Servlet API 簽名、請求/響應對象共享、瀏覽器地址欄表現、適用場景、路徑解析規則五個維度,建立清晰的心智模型,並給出生產級選擇指南。
| 維度 | request.getRequestDispatcher() (轉發) | response.sendRedirect() (重定向) |
|---|---|---|
| 方法歸屬 | ServletRequest / ServletContext |
HttpServletResponse |
| 返回類型 | RequestDispatcher 對象 |
無返回值 (void) |
| 核心方法 | forward(request, response) |
sendRedirect(location) |
| 執行位置 | 服務端內部 | 客戶端瀏覽器 |
| 請求對象 | 同一個 request(屬性共享) | 新 request(屬性丟失) |
| 響應對象 | 同一個 response | 新 response |
| 瀏覽器地址欄 | 不變(用戶無感知) | 變為目標 URL |
| HTTP 交互 | 1 次請求/響應 | 2 次請求/響應(302 + 新請求) |
| 跳轉範圍 | 僅限當前 Web 應用內部(不可跨域) | 任意 URL(可跨域、跨應用、跨協議) |
| 速度 | 快(無網絡往返) | 慢(額外一次網絡往返) |
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
String username = req.getParameter("username");
// 業務邏輯...
req.setAttribute("user", username); // 設置請求屬性
// 轉發到成功頁,共享 request
req.getRequestDispatcher("/success.jsp").forward(req, resp);
// 瀏覽器地址欄仍為 /login
}
}
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String username = req.getParameter("username");
// 業務邏輯...
// 重定向到成功頁,新請求
resp.sendRedirect(req.getContextPath() + "/success.jsp");
// 瀏覽器地址欄變為 /success.jsp
// 原 request 屬性丟失,需用 Session/Flash 屬性/URL 參數傳值
}
}
| API | 路徑類型 | 解析基準 | 示例 |
|---|---|---|---|
ServletRequest.getRequestDispatcher(path) |
相對路徑 / 絕對路徑 | 當前請求路徑 / Web 應用根路徑 | req.getRD("sub.jsp")、req.getRD("/app/sub.jsp") |
ServletContext.getRequestDispatcher(path) |
只能絕對路徑 | Web 應用根路徑 | appCtx.getRD("/app/sub.jsp")(相對路徑拋異常) |
HttpServletResponse.sendRedirect(path) |
相對路徑 / 絕對路徑 / 完整 URL | 當前請求 URL / Web 應用根路徑 / 任意 | resp.sendRedirect("sub.jsp")、resp.sendRedirect("/app/sub.jsp")、resp.sendRedirect("https://other.com") |
最佳實踐:統一使用以
/開頭的絕對路徑(相對 ContextPath),配合req.getContextPath()拼接,避免相對路徑在不同層級 Servlet 下解析錯誤。
web.xml <error-page>) | **轉發** | 容器內部轉發,保留原請求異常信息 |
| 跨應用/跨域跳轉(SSO、支付回調) | **重定向** | 唯一可行方式 |
| 下載文件/流式響應 | **無跳轉** | 直接輸出流,設置 Header |
@Controller
public class UserController {
@PostMapping("/login")
public String login(@RequestParam String username, Model model) {
// 業務邏輯...
model.addAttribute("user", username);
// 1. 轉發(默認):返回視圖名,DispatcherServlet 內部 forward
return "success";
// 2. 重定向:redirect: 前綴
// return "redirect:/success"; // 瀏覽器地址欄變化,model 屬性丟失
// 3. 重定向攜帶參數:RedirectAttributes
// RedirectAttributes ra = new RedirectAttributes();
// ra.addFlashAttribute("user", username); // Flash 屬性:存 Session,取後刪
// return "redirect:/success";
}
}
轉發 = 服務端內部流轉,共享 request/response,地址欄不變,快,僅限應用內部。
重定向 = 客戶端雙次請求,新 request/response,地址欄變化,慢,支持任意 URL。
掌握 PRG 模式(Post-Redirect-Get)、RedirectAttributes Flash 屬性、路徑解析規則,是構建健壯、用戶友好 Web 應用的基本功。