乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 SpringMVC概述与DispatcherServlet

    • 本章导读:Spring MVC概述与DispatcherServlet
    • Spring MVC 是什么
    • MVC 设计模式
    • 前端控制器模式
    • DispatcherServlet
    • 核心组件协作
  • 第2章 控制器与请求映射

    • 本章导读:控制器与请求映射
    • Controller
    • RestController
    • RequestMapping
    • GetMapping
    • PostMapping
    • PutMapping
    • DeleteMapping
    • PathVariable
    • RESTful
    • 请求映射原理
  • 第3章 请求参数获取与转换

    • 本章导读:请求参数获取与转换
    • RequestParam
    • RequestBody
    • RequestHeader
    • CookieValue
    • Model
    • ModelAttribute
    • 数据绑定原理
    • 数据校验
  • 第4章 响应数据与视图解析

    • 本章导读:响应数据与视图解析
    • ResponseBody
    • ResponseEntity
    • ModelAndView
    • ViewResolver
    • HttpMessageConverter
    • forward与redirect
  • 第5章 拦截器过滤器与跨域

    • 本章导读:拦截器、过滤器与跨域
    • HandlerInterceptor
    • WebMvcConfigurer
    • CrossOrigin
    • 登录验证实战
  • 第6章 文件上传与异常处理

    • 本章导读:文件上传与异常处理
    • MultipartFile
    • 文件下载
    • ExceptionHandler
    • ControllerAdvice
    • RestControllerAdvice
    • ResponseStatus
  • 第7章 高级特性与最佳实践

    • 本章导读:高级特性与最佳实践
    • SessionAttributes
    • SessionAttribute
    • RedirectAttributes
    • MockMvc测试
    • 国际化
    • 最佳实践
  • 第8章 扩展与异步机制

    • 本章导读:扩展与异步机制
    • 异步请求处理
    • 自定义参数解析器
    • 内容协商

RedirectAttributes

本章聚焦 Spring MVC 重定向场景下的数据传递机制。RedirectAttributes 是 Flash 属性的容器,专门解决 POST-Redirect-GET 模式中"重定向后如何传递一次性消息"的问题。它是 Spring MVC 对 Servlet 3.0 Flash Attribute 规范的封装,理解其工作原理对构建安全的表单提交流程至关重要。


定义与作用

RedirectAttributes 是 Spring MVC 提供的特殊 Model 接口,用于在**重定向(redirect)**发生前存储临时数据。这些数据会被放入 Flash 作用域,在重定向后的第一次请求中可用,随后自动清除。

核心特征:

  • Flash 属性:存储在 Session 中,但只保留一次请求,读取后自动删除
  • 两种添加方式:addFlashAttribute()(放 Flash,URL 不可见)和 addAttribute()(放 URL 查询参数)
  • 自动清理:重定向目标请求处理完毕后,Flash 属性自动从 Session 中移除

生活类比:临时访客通行证

想象飞翔科技大楼的访客系统:

  • addAttribute():把访客姓名写在 URL 地址栏里(?name=张三),所有人都能看到,但 URL 长度有限,且信息暴露在外
  • addFlashAttribute():给访客发一张临时通行证,存在大楼前台(Session)。访客到达目标楼层时出示通行证,门卫验证后立刻撕掉(自动删除)。信息不暴露在 URL 中,且只能用一次

POST-Redirect-GET 模式就像:你在前台填表(POST)→ 前台给你一张临时通行证 → 你拿着通行证去目标楼层(GET)→ 门卫看完立刻销毁通行证 → 你正常进入。如果刷新页面,通行证已经销毁,不会重复办理业务。


核心原理

Flash 属性传递流程

关键机制:

  1. 保存阶段:POST 请求处理完毕后,FlashMapManager 将 RedirectAttributes 中的 Flash 属性存入 Session
  2. 恢复阶段:重定向后的 GET 请求到达时,FlashMapManager 从 Session 中读取 Flash 属性并放入当前请求的 Model,同时从 Session 中删除
  3. URL 参数:addAttribute() 添加的属性会被拼接到重定向 URL 的查询字符串中,对客户端可见

适用位置与常用属性

RedirectAttributes 作为方法参数类型使用,Spring MVC 自动注入。

方法作用数据去向生命周期典型用途
addFlashAttribute(String name, Object value)添加 Flash 属性Session(FlashMap)一次请求后自动清除成功/错误提示消息
addFlashAttribute(Object value)添加 Flash 属性(以类型为隐式名称)Session(FlashMap)一次请求后自动清除传递复杂对象
addAttribute(String name, Object value)添加 URL 参数重定向 URL 查询字符串永久(在 URL 中)传递 ID 等必要参数

addFlashAttribute vs addAttribute 对比

维度addFlashAttributeaddAttribute
存储位置Session(临时 FlashMap)URL 查询参数
客户端可见❌ 不可见✅ 可见
刷新后保留❌ 不保留(已清除)✅ 保留(在 URL 中)
数据类型任意对象只能转为字符串
长度限制无(受 Session 限制)受 URL 长度限制
安全性高(不暴露数据)低(数据在 URL 中)

完整示例

场景

飞翔科技员工管理系统的"新增员工"页面采用 POST-Redirect-GET 模式。后端小崔使用 RedirectAttributes 在重定向后传递"创建成功"的提示消息,避免用户刷新页面导致表单重复提交。

控制器

package com.feixiang.web.controller;

import com.feixiang.web.entity.EmployeeForm;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @PostMapping("/create")
    public String createEmployee(@ModelAttribute EmployeeForm form,
                                 RedirectAttributes redirectAttributes) {
        // 保存员工
        Long employeeId = employeeService.save(form);

        // Flash 属性:成功消息,只显示一次
        redirectAttributes.addFlashAttribute("successMessage",
            "员工 " + form.getName() + " 创建成功!");

        // URL 参数:员工 ID,用于详情页查询
        redirectAttributes.addAttribute("id", employeeId);

        return "redirect:/employee/detail";
    }

    @GetMapping("/detail")
    public String detailPage(@RequestParam Long id, Model model) {
        // successMessage 会自动从 Flash 属性恢复到 Model
        if (!model.containsAttribute("successMessage")) {
            model.addAttribute("employee", employeeService.findById(id));
        }
        return "employee/detail";
    }
}

请求示例一:POST 创建员工(触发重定向)

curl -X POST http://localhost:8080/employee/create \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "name=张三&department=研发部&age=25" \
  -c cookies.txt -L -v

响应过程:

< HTTP/1.1 302
< Location: /employee/detail?id=1001
--- 自动跟随重定向 ---
> GET /employee/detail?id=1001
< HTTP/1.1 200
< Content-Type: text/html

<!-- 页面显示:员工 张三 创建成功! -->

请求示例二:直接 GET 详情页(无 Flash 属性)

curl -X GET "http://localhost:8080/employee/detail?id=1001" \
  -b cookies.txt

响应:详情页面正常显示,不显示"创建成功"消息(Flash 属性已在上次请求后清除)。

错误提示场景

@PostMapping("/create")
public String createEmployee(@ModelAttribute EmployeeForm form,
                             RedirectAttributes redirectAttributes) {
    if (employeeService.existsByName(form.getName())) {
        // 校验失败,重定向回表单页,携带错误消息
        redirectAttributes.addFlashAttribute("errorMessage", "员工姓名已存在");
        redirectAttributes.addFlashAttribute("form", form);  // 回显表单数据
        return "redirect:/employee/new";
    }

    Long employeeId = employeeService.save(form);
    redirectAttributes.addFlashAttribute("successMessage", "创建成功");
    redirectAttributes.addAttribute("id", employeeId);
    return "redirect:/employee/detail";
}
curl -X POST http://localhost:8080/employee/create \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "name=张三&department=研发部" \
  -c cookies.txt -L

如果"张三"已存在,重定向到 /employee/new,页面显示错误消息并回显表单数据。


易错场景与面试考点

误区一:在 addFlashAttribute 后尝试在重定向前读取

错误认知:"我 addFlashAttribute 之后,在当前请求里就能从 Model 中读到它。"

纠正:addFlashAttribute 的数据在当前 POST 请求的 Model 中不可见。它只在重定向后的 GET 请求中通过 Model 恢复。如果需要在当前请求中传递数据给视图,应该直接用 model.addAttribute()。

误区二:用 addAttribute 传递敏感信息

错误代码:

redirectAttributes.addAttribute("password", form.getPassword());  // ❌ 危险!
return "redirect:/employee/detail";

问题:addAttribute 的数据会暴露在 URL 中(?password=123456),浏览器历史、服务器日志、Referer 中都会留下痕迹。

纠正:敏感信息必须使用 addFlashAttribute,或者根本不通过重定向传递。

误区三:重定向到外部 URL 时使用 Flash 属性

错误代码:

redirectAttributes.addFlashAttribute("token", secretToken);
return "redirect:https://external.com/callback";  // ❌ Flash 属性会丢失!

问题:Flash 属性依赖 Session,而 Session 是域内(domain)的。重定向到外部域名时,Session 不共享,Flash 属性无法传递。

纠正:跨域重定向只能使用 addAttribute(URL 参数),且不应传递敏感信息。

面试高频:为什么 POST 请求后要 Redirect?

问题:表单提交后为什么要 return "redirect:/xxx" 而不是直接返回视图?

标准回答:

  1. 防止重复提交:如果直接返回视图,用户刷新页面会重新提交表单(重复 POST)
  2. URL 规范化:重定向后浏览器地址栏变为 GET 请求的 URL,符合 REST 语义
  3. 书签友好:用户可以把重定向后的页面加入书签
  4. 配合 Flash 属性:可以在重定向后传递一次性的成功/错误提示

这就是经典的 POST-Redirect-GET(PRG) 模式。


小结

RedirectAttributes 是 Spring MVC 为 POST-Redirect-GET 模式提供的 Flash 属性容器。addFlashAttribute() 将数据存入 Session 级别的 FlashMap,在重定向后的第一次请求中自动恢复到 Model 并清除;addAttribute() 将数据拼接到重定向 URL 的查询字符串中。前者适合传递提示消息,后者适合传递必要的查询参数。

本章与全局的关系:本章讲解了重定向场景的一次性数据传递。它与上一章的 @SessionAttributes(持久会话数据)形成对比:Flash 属性是"用一次就扔",SessionAttributes 是"保持到显式清理"。下一章"MockMvc 测试"将讲解如何在无需启动 Servlet 容器的情况下测试这些 Web 请求处理逻辑。

上一页
SessionAttribute
下一页
MockMvc测试