1. 概述

本文深入探讨在 Spring 中实现 重定向(Redirect) 的多种方式,并分析每种方案背后的适用场景与设计考量。内容涵盖 RedirectViewredirect: 前缀、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)拼接到跳转 URL
  • addFlashAttribute() 添加的参数不会出现在 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. 转发时传递参数

转发时可通过 HttpServletRequestsetAttribute() 传递数据。

源接口:

@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&param2=two&attribute=forwardedWithParams

✅ 参数通过服务器内部转发成功传递。

10. 总结

本文系统梳理了 Spring 中重定向与转发的核心技术方案:

  • ✅ **日常开发推荐 redirect: 前缀 + RedirectAttributes**:简洁、低耦合、功能完整
  • ✅ **敏感数据用 addFlashAttribute**:避免 URL 暴露
  • POST 重定向注意状态码选择:307/308 保持 POST 方法
  • 转发用于内部跳转:减少客户端请求,URL 不变

所有示例代码已整理至 GitHub 仓库:https://github.com/yourname/spring-redirect-examples(模拟地址)


原始标题:A Guide To Spring Redirects | Baeldung