请求转发与重定向:Java Web 中的页面跳转机制
在 Java Web 开发中,页面跳转是常见需求,主要通过请求转发(Forward) 和请求重定向(Redirect) 两种方式实现。它们在原理、使用场景和特性上有显著区别,理解这些差异对构建高效的 Web 应用至关重要。本文将详细解析两种机制的实现、区别及适用场景。
请求转发(Forward)
请求转发是指服务器收到客户端请求后,将请求 “转发” 给内部另一个资源(如 Servlet、JSP)处理,最终由目标资源生成响应返回给客户端。整个过程在服务器内部完成,客户端感知不到中间转发步骤。
实现方式
通过 HttpServletRequest 的 getRequestDispatcher() 方法获取 RequestDispatcher 对象,再调用其 forward() 方法实现转发:
1 | // 请求转发到 /targetServlet |
RequestDispatcher 接口
RequestDispatcher 由 Servlet 容器创建,用于封装目标资源并提供转发或包含功能,核心方法:
forward(request, response):将请求转发到目标资源,目标资源的响应会直接返回给客户端。include(request, response):将目标资源的响应包含到当前响应中(如页面片段复用)。
接口中定义了多个常量(如 FORWARD_REQUEST_URI、INCLUDE_CONTEXT_PATH),用于在转发 / 包含时传递原始请求的元信息(如 URI、路径等)。
转发的特性
- 一次请求:客户端仅发起一次请求,服务器内部转发,
request和response对象在整个过程中复用。 - 地址栏不变:客户端地址栏显示的仍是初始请求的 URL,不显示目标资源的路径。
- 共享
request数据:转发过程中,request的attribute可在多个资源间共享(通过setAttribute/getAttribute)。 - 仅限内部资源:只能转发到当前 Web 应用内的资源(如
/target.jsp、/servlet/demo),无法跨应用或跨域。 - 路径规则:转发路径中的
/代表当前 Web 应用的根目录(如/target等价于http://localhost:8080/应用名/target)。
示例:转发时共享数据
1 | // 源 Servlet |
请求重定向(Redirect)
请求重定向是指服务器收到客户端请求后,返回一个特殊的响应(状态码 302 + Location 头),告知客户端 “请重新访问 Location 指向的 URL”。客户端收到响应后,会主动发起第二次请求,访问新的 URL。
实现方式
通过 HttpServletResponse 的 sendRedirect() 方法直接指定重定向目标 URL:
1 | // 重定向到 /login.jsp |
也可手动设置状态码和 Location 头实现同样效果:
1 | response.setStatus(HttpServletResponse.SC_FOUND); // 302 状态码 |
重定向的特性
两次请求:客户端先发起一次请求,收到重定向响应后再发起第二次请求,两次请求使用不同的
request对象。地址栏变化:客户端地址栏会更新为
Location指向的 URL。不共享数据:两次请求是独立的,
request的attribute无法传递(需通过 URL 参数或会话共享数据)。跨域 / 跨应用:可重定向到任意 URL(如其他 Web 应用、外部网站
https://example.com)。路径规则:重定向路径中的
/代表服务器的根目录(如 Tomcat 中/等价于http://localhost:8080/),需通过request.getContextPath()拼接当前应用路径:1
2// 正确重定向到当前应用内的 /user 路径
response.sendRedirect(request.getContextPath() + "/user");
示例:重定向时传递参数
1 | // 重定向并通过 URL 参数传递数据 |
请求转发与重定向的核心区别
| 特性 | 请求转发(Forward) | 请求重定向(Redirect) |
|---|---|---|
| 请求次数 | 1 次(服务器内部转发) | 2 次(客户端重新请求) |
| 地址栏 URL | 不变(显示初始请求 URL) | 变化(显示目标 URL) |
request 共享 |
共享(同一对象) | 不共享(两次请求是不同对象) |
| 跳转范围 | 仅限当前 Web 应用内 | 可跨应用、跨域 |
路径中的 / 含义 |
代表当前 Web 应用根目录(/应用名/) |
代表服务器根目录(http://主机:端口/) |
| 响应头提交时机 | 转发前未提交响应头 | 立即提交响应头(后续无法修改响应头) |
| 适用场景 | 内部资源跳转(如 MVC 中控制器跳视图) | 登录后跳转、避免表单重复提交、跨应用跳转 |
适用场景分析
- 请求转发的典型场景:
- MVC 模式中,控制器(Servlet)处理完业务后,转发到视图(JSP)渲染页面。
- 多个组件协作处理同一请求(如权限校验 Servlet 转发到业务处理 Servlet)。
- 需要共享
request数据时(如传递处理结果到视图)。
- 请求重定向的典型场景:
- 用户登录成功后跳转到首页(避免刷新页面导致重复提交登录表单)。
- 访问未授权资源时,重定向到登录页。
- 跨应用跳转(如从电商系统跳转到支付系统)。
常见问题与最佳实践
路径拼接问题:
转发时,推荐使用相对路径或
/开头的应用内路径(如/target)。重定向时,必须通过request.getContextPath()拼接应用路径,避免部署路径变化导致错误:
1
2
3
4// 错误方式(依赖部署路径)
response.sendRedirect("/app/user");
// 正确方式(动态获取应用路径)
response.sendRedirect(request.getContextPath() + "/user");
响应头提交后修改的异常:
- 重定向后无法再修改响应头或响应体(会抛出
IllegalStateException)。 - 转发前若已写入响应体(如
out.print()),转发会失败(需确保转发前未提交响应)。
- 重定向后无法再修改响应头或响应体(会抛出
避免表单重复提交:
- 表单提交后,使用重定向到结果页(而非转发),防止用户刷新页面导致重复提交
v1.3.10