请求转发与响应重定向教程
1 概述
请求转发和响应重定向是 Web 应用中实现资源间接访问、控制页面跳转的两种核心手段。
-
请求转发:通过
HttpServletRequest对象实现,是服务器内部行为。- 生活举例:张三找李四借钱,李四没钱,李四直接找王五借,最后把钱给张三(张三全程只找了李四一次)。
-
响应重定向:通过
HttpServletResponse对象实现,是客户端行为。- 生活举例:张三找李四借钱,李四没钱,让张三自己去找王五借(张三又单独找了王五)。
2 请求转发
请求转发特点(核心)
服务器内部行为,客户端地址栏不变。
客户端只发送1 次请求,服务端只有 1 对
Request/Response对象。请求参数、请求域数据可以传递。
可以访问
WEB\-INF下的受保护资源。不能访问项目外部资源。
请求转发代码示例
1. ServletA(转发发起方)
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取请求参数(演示参数传递)
String username = req.getParameter("username");
System.out.println("ServletA 收到参数:username = " + username);
// 2. 向请求域中存入数据(演示域数据传递)
req.setAttribute("reqMsg", "这是ServletA存入请求域的数据");
// 3. 获取请求转发器,转发给 ServletB
// 也可以转发给静态资源:req.getRequestDispatcher("welcome.html")
// 也可以转发给WEB-INF下资源:req.getRequestDispatcher("WEB-INF/secret.html")
req.getRequestDispatcher("servletB").forward(req, resp);
}
}
2. ServletB(转发目标方)
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取 ServletA 传递的请求参数
String username = req.getParameter("username");
System.out.println("ServletB 收到参数:username = " + username);
// 2. 获取 ServletA 存入请求域的数据
String reqMsg = (String) req.getAttribute("reqMsg");
System.out.println("ServletB 收到域数据:" + reqMsg);
// 3. 给客户端响应
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("ServletB 响应成功!<br>");
writer.write("收到参数:username = " + username + "<br>");
writer.write("收到域数据:" + reqMsg);
}
}
测试步骤
启动项目,浏览器访问:
http://localhost:8080/你的项目名/servletA?username=zhangsan
观察:
地址栏保持 ****
/servletA**** 不变。页面显示 ServletB 的响应,且能看到参数和域数据。
3 响应重定向
响应重定向特点(核心)
客户端行为,服务端通过
302响应码 + 路径,让客户端重新请求新资源。客户端发送至少 2 次请求,地址栏会变化。
请求参数、请求域数据不能传递(因为是新的 Request 对象)。
不能访问
WEB\-INF下的受保护资源。可以访问项目外部资源(如百度、尚硅谷官网)。
响应重定向代码示例
1. ServletA(重定向发起方)
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取请求参数(重定向后无法传递)
String username = req.getParameter("username");
System.out.println("ServletA 收到参数:username = " + username);
// 2. 向请求域中存入数据(重定向后无法传递)
req.setAttribute("reqMsg", "这是ServletA存入请求域的数据");
// 3. 响应重定向到 ServletB
// 也可以重定向到外部资源:resp.sendRedirect("http://www.atguigu.com");
resp.sendRedirect("servletB");
}
}
2. ServletB(重定向目标方)
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 尝试获取参数(重定向后为 null)
String username = req.getParameter("username");
System.out.println("ServletB 收到参数:username = " + username);
// 2. 尝试获取域数据(重定向后为 null)
String reqMsg = (String) req.getAttribute("reqMsg");
System.out.println("ServletB 收到域数据:" + reqMsg);
// 3. 给客户端响应
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("ServletB 响应成功!<br>");
writer.write("收到参数:username = " + username + "<br>");
writer.write("收到域数据:" + reqMsg);
}
}
测试步骤
启动项目,浏览器访问:
http://localhost:8080/你的项目名/servletA?username=zhangsan
观察:
地址栏变成 ****
/servletB。页面显示参数和域数据均为
null。
9.4 核心区别对比表
| 对比维度 | 请求转发 | 响应重定向 |
|---|---|---|
| 请求次数 | 1 次 | 至少 2 次 |
| 地址栏变化 | 不变 | 变化 |
| 参数 / 域数据传递 | 可以传递 | 不能传递 |
| 访问 WEB-INF 资源 | 可以 | 不可以 |
| 访问外部资源 | 不可以 | 可以 |
| 行为主体 | 服务器 | 客户端 |
9.5 常见问题:GET/POST 支持与参数传递
1. 请求转发:GET/POST 都完美支持
无论客户端是 GET 还是 POST 请求 ServletA,转发到 ServletB 后,请求方法保持不变(ServletA 是 POST,ServletB 收到的也是 POST)。
请求参数(无论是 URL 后的 GET 参数,还是表单的 POST 参数),都能完整传递。
2. 响应重定向:GET/POST 有细节
-
重定向后的请求方法默认是 GET:
如果客户端是 POST 请求 ServletA,重定向到 ServletB 后,ServletB 收到的是 GET 请求。
如果客户端是 GET 请求 ServletA,重定向后还是 GET。
-
参数传递技巧:
-
如果一定要在重定向时传参,可以手动拼在 URL 后面:
resp.sendRedirect("servletB?username=" + username); 但注意:URL 传参有长度限制,且敏感数据会暴露在地址栏。
-