1. 概述
本文深入探讨在 Spring 中实现 重定向(Redirect) 的多种方式,并分析每种方案背后的适用场景与设计考量。内容涵盖 RedirectView
、redirect:
前缀、forward:
前缀、RedirectAttributes
的使用,以及 POST 请求重定向等高级用法。
目标是帮助有经验的开发者在实际项目中避开常见“坑”,选择最合适的技术路径。
2. 为什么要使用重定向?
在 Spring Web 应用中,重定向是一种常见的控制流手段。典型场景包括:
- ✅ 防止表单重复提交:使用经典的 Post-Redirect-Get 模式
- ✅ 流程跳转:将请求委托给另一个控制器方法处理
- ✅ URL 重写或迁移:旧接口跳转到新接口
⚠️ 注意:Post-Redirect-Get 模式虽然常用,但并不能完全避免重复提交。例如用户在首次提交未完成时刷新页面,仍可能导致多次提交。更彻底的解决方案需结合 Token 机制。
3. 使用 RedirectView 实现重定向
最直接的方式是返回 RedirectView
对象。
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/redirectWithRedirectView")
public RedirectView redirectWithUsingRedirectView(
RedirectAttributes attributes) {
attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectView");
attributes.addAttribute("attribute", "redirectWithRedirectView");
return new RedirectView("redirectedUrl");
}
}
关键点解析:
RedirectAttributes
可注入方法参数,框架自动管理addAttribute()
添加的参数会作为查询参数(query param)拼接到跳转 URLaddFlashAttribute()
添加的参数不会出现在 URL 中,而是通过 session 临时存储(下文详述)- 底层调用
HttpServletResponse.sendRedirect()
,返回 HTTP 302
测试验证:
curl -i http://localhost:8080/spring-rest/redirectWithRedirectView
响应:
HTTP/1.1 302 Found
Location: http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView
✅ 优点:控制精细
❌ 缺点:控制器直接依赖 Spring API,耦合度高
4. 使用 redirect: 前缀(推荐)
更优雅的方式是使用 redirect:
前缀,解耦控制器与重定向逻辑。
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/redirectWithRedirectPrefix")
public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) {
model.addAttribute("attribute", "redirectWithRedirectPrefix");
return new ModelAndView("redirect:/redirectedUrl", model);
}
}
核心机制:
UrlBasedViewResolver
及其子类识别redirect:
前缀- 前缀后的部分作为目标 URL
- ✅ 控制器无需感知重定向行为,逻辑更清晰
路径说明:
redirect:/redirectedUrl
→ 相对于当前 Servlet 上下文(相对路径)redirect:http://example.com/xxx
→ 绝对 URL 跳转
测试结果:
curl -i http://localhost:8080/spring-rest/redirectWithRedirectPrefix
HTTP/1.1 302 Found
Location: http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectPrefix
✅ 推荐在大多数场景下使用此方式,简洁且低耦合。
5. 使用 forward: 前缀实现转发
与重定向不同,转发(Forward) 是服务器内部行为,客户端无感知。
转发 vs 重定向:
类型 | 发生位置 | 请求次数 | URL 是否变化 | 典型状态码 |
---|---|---|---|---|
Redirect | 客户端 | 2次 | 是 | 302 |
Forward | 服务器内部 | 1次 | 否 | 200 |
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/forwardWithForwardPrefix")
public ModelAndView redirectWithUsingForwardPrefix(ModelMap model) {
model.addAttribute("attribute", "forwardWithForwardPrefix");
return new ModelAndView("forward:/redirectedUrl", model);
}
}
实现原理:
forward:
前缀被UrlBasedViewResolver
解析- 创建
InternalResourceView
,调用RequestDispatcher.forward()
测试:
curl -I http://localhost:8080/spring-rest/forwardWithForwardPrefix
若 /redirectedUrl
未定义 GET 接口,可能返回:
HTTP/1.1 405 Method Not Allowed
⚠️ 注意:转发是服务器内部跳转,原始请求方法(GET/POST)保持不变,但 URL 不会变化。
6. 使用 RedirectAttributes 传递参数
重定向时参数传递是高频需求,RedirectAttributes
是官方推荐方案。
@GetMapping("/redirectWithRedirectAttributes")
public RedirectView redirectWithRedirectAttributes(RedirectAttributes attributes) {
attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectAttributes");
attributes.addAttribute("attribute", "redirectWithRedirectAttributes");
return new RedirectView("redirectedUrl");
}
两种属性类型:
addAttribute()
→ 拼接为 URL 参数(公开)addFlashAttribute()
→ 存入 session,跳转后自动清除(私密)
接收 Flash 属性:
@GetMapping("/redirectedUrl")
public ModelAndView redirection(
ModelMap model,
@ModelAttribute("flashAttribute") Object flashAttribute) {
model.addAttribute("redirectionAttribute", flashAttribute);
return new ModelAndView("redirection", model);
}
测试响应:
HTTP/1.1 302 Found
Set-Cookie: JSESSIONID=...; Path=/spring-rest/
Location: http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectAttributes
✅ 优势:安全传递敏感数据,避免 URL 暴露
7. 不使用前缀的配置方式(XML 配置)
通过 XmlViewResolver
可实现无需 redirect:
前缀的跳转。
配置文件(spring-views.xml):
<bean class="org.springframework.web.servlet.view.XmlViewResolver">
<property name="location">
<value>/WEB-INF/spring-views.xml</value>
</property>
<property name="order" value="0" />
</bean>
替代默认的 InternalResourceViewResolver
。
定义 RedirectView Bean:
<bean id="RedirectedUrl" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="redirectedUrl" />
</bean>
控制器代码:
@Controller
@RequestMapping("/")
public class RedirectController {
@GetMapping("/redirectWithXMLConfig")
public ModelAndView redirectWithUsingXMLConfig(ModelMap model) {
model.addAttribute("attribute", "redirectWithXMLConfig");
return new ModelAndView("RedirectedUrl", model);
}
}
测试:
curl -i http://localhost:8080/spring-rest/redirectWithXMLConfig
结果与前文一致。
⚠️ 注意:此方式适用于传统 XML 配置项目,现代 Spring Boot 项目建议使用 Java Config。
8. 重定向 HTTP POST 请求
某些场景(如支付回调)需将 POST 请求重定向为另一个 POST 请求。
HTTP 状态码语义:
状态码 | 名称 | 是否允许方法变更 |
---|---|---|
301 | Moved Permanently | 允许(POST→GET) |
302 | Found | 允许(POST→GET) |
307 | Temporary Redirect | ❌ 禁止 |
308 | Permanent Redirect | ❌ 禁止 |
代码实现(POST → POST):
@PostMapping("/redirectPostToPost")
public ModelAndView redirectPostToPost(HttpServletRequest request) {
request.setAttribute(
View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
return new ModelAndView("redirect:/redirectedPostToPost");
}
@PostMapping("/redirectedPostToPost")
public ModelAndView redirectedPostToPost() {
return new ModelAndView("redirection");
}
测试命令:
curl -L --verbose -X POST http://localhost:8080/spring-rest/redirectPostToPost
-L
参数表示跟随重定向,最终会以 POST 方法请求目标接口。
9. 转发时传递参数
转发时可通过 HttpServletRequest
的 setAttribute()
传递数据。
源接口:
@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
public ModelAndView forwardWithParams(HttpServletRequest request) {
request.setAttribute("param1", "one");
request.setAttribute("param2", "two");
return new ModelAndView("forward:/forwardedWithParams");
}
目标接口(可在不同 Controller):
@Controller
@RequestMapping("/")
public class RedirectParamController {
@RequestMapping(value = "/forwardedWithParams", method = RequestMethod.GET)
public RedirectView forwardedWithParams(
final RedirectAttributes redirectAttributes, HttpServletRequest request) {
redirectAttributes.addAttribute("param1", request.getAttribute("param1"));
redirectAttributes.addAttribute("param2", request.getAttribute("param2"));
redirectAttributes.addAttribute("attribute", "forwardedWithParams");
return new RedirectView("redirectedUrl");
}
}
测试结果:
curl -i http://localhost:8080/spring-rest/forwardWithParams
HTTP/1.1 302 Found
Location: http://localhost:8080/spring-rest/redirectedUrl?param1=one¶m2=two&attribute=forwardedWithParams
✅ 参数通过服务器内部转发成功传递。
10. 总结
本文系统梳理了 Spring 中重定向与转发的核心技术方案:
- ✅ **日常开发推荐
redirect:
前缀 +RedirectAttributes
**:简洁、低耦合、功能完整 - ✅ **敏感数据用
addFlashAttribute
**:避免 URL 暴露 - ✅ POST 重定向注意状态码选择:307/308 保持 POST 方法
- ✅ 转发用于内部跳转:减少客户端请求,URL 不变
所有示例代码已整理至 GitHub 仓库:https://github.com/yourname/spring-redirect-examples(模拟地址)