Spring 跨重定向请求传递数据
在处理完POST请求后, 通常来讲一个最佳实践就是执行一下重定向。除了其他的一些因素外,这样做能够防止用户点击浏览器的刷新按钮或后退箭头时,客户端重新执行危险的POST请求。
在控制器方法返回的视图名称中,我们借助了“ redirect:” 前缀的力量。当控制器方法返回的String 值 以“ redirect:” 开头 的 话, 那么 这个 String 不是 用来 查找 视图 的, 而是 用来 指导 浏览器 进行 重定向 的 路径。 我们 可以 回头 看一下 程序 清单 5. 17, 可以看到 processRegistration() 方法 返回 的“ redirect: String” 如下 所示:
return "redirect:/ spitter/" + spitter. getUsername();
“redirect:” 前缀 能够 让 重定向 功能 变得 非常 简单。 你 可能 会想 Spring 很难 再让 重定向 功能 变得 更简单 了。 但是, 请 稍等: Spring 为重 定向 功能 还 提供 了 一些 其他 的 辅助 功能。 具体 来讲, 正在 发起 重定向 功能 的 方法 该 如何 发送 数据 给 重定向 的 目标 方法 呢? 一般 来讲, 当 一个 处理器 方法 完成 之后, 该 方法 所指 定的 模型 数据 将会 复制 到 请求 中, 并作 为 请求 中的 属性, 请求 会 转发( forward) 到 视图 上进 行 渲染。 因为 控制器 方法 和 视图 所 处理 的 是 同一个 请求, 所以 在 转发 的 过程中, 请求 属性 能够 得以 保存。
但是, 当 控制器 的 结果是 重定向 的 话, 原始 的 请求 就 结束 了, 并且 会 发起 一个 新的 GET 请求。 原始 请求 中 所带 有的 模型 数据 也就 随着请求一起 消亡 了。 在 新的 请求 属性 中, 没有 任何 的 模型 数据, 这个 请求 必须 要 自己 计算 数据。

图 1 模型 的 属性 是以 请求 属性 的 形式 存放 在 请求 中的, 在 重定向后无法存活
显然, 对于 重定向 来说, 模型 并不能 用来 传递 数据。 但是 我们 也有 一些 其他 方案, 能够 从 发起 重定向 的 方法 传递 数据 给 处理 重定向 方法 中: 使用 URL 模板 以 路径 变量 和/ 或 查询 参数 的 形式 传递 数据; 通过 flash 属性 发送 数据。
首先, 我们 看一下 Spring 如何 帮助 我们 通过 路径 变量 和/ 或 查询 参数 的 形式 传递 数据。
通过URL模板进行重定向
通过 路径 变量 和 查询 参数 传递 数据 看起来 非常 简单。 例如, 在 程序 清单 5. 19 中, 我们 以 路径 变量 的 形式 传递 了 新 创建 Spitter 的 username。 但是 按照 现在 的 写法, username 的 值 是 直接 连接 到 重定向 String 上 的。 这 能够 正常 运行, 但是 还 远远 不能 说 没有 问题。 当 构建 URL 或 SQL 查询 语句 的 时候, 使用 String 连接 是 很 危险 的。
return "redirect:/ spitter/{ username}";
除了 连接 String 的 方式 来 构建 重定向 URL, Spring 还 提供 了 使用 模板 的 方式 来 定义 重定向 URL。 例如, 在 程序 清单 5. 19 中, processRegistration() 方法 的 最后 一行 可以 改写 为 如下 的 形式:
@RequestMapping( value="/ register", method= POST)
public String processRegistration( Spitter spitter, Model model) {
spitterRepository. save( spitter);
model. addAttribute(" username", spitter. getUsername());
return "redirect:/ spitter/{ username}";
}
现在, username 作为 占位符 填充 到了 URL 模板 中, 而 不是 直接 连接 到 重定向 String 中, 所以 username 中 所有 的 不安全 字符 都会 进行 转义。 这样 会 更加 安全, 这里 允许 用户 输入 任何 想要 的 内容 作为 username, 并 会 将其 附加 到 路径 上。
除此之外, 模型 中 所有 其他 的 原始 类型 值 都可以 添加 到 URL 中 作为 查询 参数。 作为 样 例, 假设 除了 username 以外, 模型 中 还要 包含 新 创建 Spitter 对象 的 id 属性, 那 processRegistration() 方法 可以 改写 为 如下 的 形式:
@RequestMapping( value="/ register", method= POST)
public String processRegistration( Spitter spitter, Model model) {
spitterRepository. save( spitter);
model. addAttribute(" username", spitter. getUsername());
model. addAttribute(" spitterId", spitter. getId());
return "redirect:/ spitter/{ username}";
}
所 返回 的 重定向 String 并没有 太大 的 变化。 但是, 因为 模型 中的 spitterId 属性 没有 匹配 重定向 URL 中的 任何 占位符, 所以 它 会 自动 以 查询 参数 的 形式 附加 到 重定向 URL 上。
如果 username 属 性的 值 是 habuma 并且 spitterId 属 性的 值 是 42, 那么 结果 得到 的 重定向 URL 路径 将会 是“/ spitter/ habuma? spitterId= 42”。
通过 路径 变量 和 查询 参数 的 形式 跨 重定向 传递 数据 是 很 简单 直接 的 方式, 但它 也有 一定 的 限制。 它 只能 用来 发送 简单 的 值, 如 String 和 数字 的 值。 在 URL 中, 并没有 办法 发送 更为 复杂 的 值, 但这 正是 flash 属性 能够 提供 帮助 的 领域。
使用 flash 属性
假设 我们 不 想在 重定向 中 发送 username 或 ID 了, 而是 要 发送 实际 的 Spitter 对象。 如果 我们 只 发送 ID 的 话, 那么 处理 重定向 的 方法 还需 要从 数据库 中 查找 才能 得到 Spitter 对象。 但是, 在 重定向 之前, 我们 其实 已经 得到 了 Spitter 对象。 为什么 不 将其 发送 给 处理 重定向 的 方法, 并将 其 展现 出来 呢?
Spitter 对象 要比 String 和 int 更为 复杂。 因此, 我们 不能 像 路径 变量 或 查询 参数 那么 容易 地 发送 Spitter 对象。 它 只能 设置 为 模型 中的 属性。
但是, 正如 我们 前面 所 讨论 的 那样, 模型 数据 最终 是以 请求 参数 的 形式 复制 到 请求 中的, 当 重定向 发生 的 时候, 这些 数据 就会 丢失。 因此, 我们 需要 将 Spitter 对象 放到 一个 位置, 使其 能够 在 重定向 的 过程中 存活 下来。
有个 方案 是将 Spitter 放到 会话 中。 会话 能够 长期存在, 并且 能够 跨 多个 请求。 所以 我们 可以 在 重定向 发生 之前 将 Spitter 放到 会话 中, 并在 重定 向后, 从 会话 中将 其 取出。 当然, 我们 还要 负责 在 重定向 后 在 会话 中将 其 清理 掉。
实际上, Spring 也 认为 将 跨 重定向 存活 的 数据 放到 会话 中 是一 个 很不 错的 方式。 但是, Spring 认为 我们 并不 需要 管理 这些 数据, 相反, Spring 提供 了 将 数据 发送 为 flash 属性( flash attribute) 的 功能。 按照 定义, flash 属性 会 一直 携带 这些 数据 直到 下一 次 请求, 然后 才会 消失。
Spring 提供 了 通过 RedirectAttributes 设置 flash 属性 的 方法, 这是 Spring 3. 1 引入 的 Model 的 一个 子 接口。 RedirectAttributes 提供 了 Model 的 所有 功能, 除此之外, 还有 几个 方法 是 用来 设置 flash 属性 的。
具体 来讲, RedirectAttributes 提供 了 一组 addFlashAttribute() 方法 来 添加 flash 属性。 重新 看一下 processRegistration() 方法, 我们 可以 使用 addFlashAttribute() 将 Spitter 对象 添加 到 模型 中:
@RequestMapping( value="/ register", method= POST)
public String processRegistration( Spitter spitter, RedirectAttributes model) {
spitterRepository. save( spitter);
model. addAttribute(" username", spitter. getUsername());
model. addFlashAttribute(" spitter", spitter);
return "redirect:/ spitter/{ username}";
}
在这里, 我们 调用 了 addFlashAttribute() 方法, 并将 spitter 作为 key, Spitter 对象 作为 值。 另外, 我们 还可以 不 设置 key 参数, 让 key 根据 值 的 类型类型 自行 推断 得出:
model. addFlashAttribute( spitter);
因为 我们 传递 了 一个 Spitter 对象 给 addFlashAttribute() 方法, 所以 推断 得到 的 key 将会 是 spitter。
在 重定向 执行 之前, 所有 的 flash 属性 都会 复制 到会 话中。 在 重定 向后, 存在 会话 中的 flash 属性 会被 取出, 并从 会话 转移 到 模型 之中。 处理 重定向 的 方法 就能 从 模型 中 访问 Spitter 对象 了, 就 像 获取 其他 的 模型 对象 一样。 图 2阐述 了 它是 如何 运行 的。

图 2 flash 属性 保存 在 会话 中, 然后 再放 到 模型 中, 因此 能够 在 重定向 的 过程中 存活
为了 完成 flash 属性 的 流程, 如下 展现 了 更新 版本 的 showSpitterProfile() 方法, 在 从 数据库 中 查找 之前, 它 会 首先 从 模型 中 检查 Spitter 对象:
@RequestMapping( value="/{ username}", method= GET)
public String showSpitterProfile( @PathVariable String username, Model model) {
if (!model. containsAttribute(" spitter")) {
model. addAttribute( spitterRepository. findByUsername( username));
}
return "profile";
}
可以 看到, showSpitterProfile() 方法 所做 的 第一 件事 就是 检查 是否 存有 key 为 spitter 的 model 属性。 如果 模型 中 包含 spitter 属性, 那就 什么 都不 用 做了。 这里 面 包含 的 Spitter 对象 将会 传递 到 视图 中进 行 渲染。 但是 如果 模型 中 不 包含 spitter 属 性的 话, 那么 showSpitterProfile() 将会 从 Repository 中 查找 Spitter, 并将 其 存放 到 模型 中。
摘自:[美] Craig Walls 沃尔斯. Spring实战(第4版)
Spring 跨重定向请求传递数据的更多相关文章
- Spring 梳理-跨重定向请求传递数据-Flash
Spring MVC Flash Attribute 的讲解与使用示例 1. Spring MVC 3.1版本加了一个很有用的特性,Flash属性,它能解决一个长久以来缺少解决的问题,一个POST/R ...
- Spring之跨重定向请求传递数据
摘要 在开发场景中,大部分数据都是使用请求转发(forward)进行传递,而使用重定向(redirect)传递数据可能比较少. 那么问题来了:请求中的数据生命周期存活时间只在一个请求转发(reques ...
- SpringMVC跨重定向请求传递数据
(1)使用URL模板以路径变量和查询参数的形式传递数据(一些简单的数据) @GetMapping("/home/index") public String index(Model ...
- spring跨重定向传递数据
spring跨重定向传递数据 为何要重定向? 作用之一:防止表单重复提交 如何重定向? // 在控制器方法返回的视图名称中,以redirect:开头的String不是用来查找视图的,而是用来指导浏览器 ...
- Jquery跨域请求php数据(jsonp)
Jquery跨域请求php数据 我们一般用到ajax的时候是在同服务器下,一般情况下不会跨域,但有时候需要调用其他域名或ip下的数据的时候,遇到跨域请求数据的时候. 今天在工作中碰到javascrip ...
- jQuery使用ajax跨域请求获取数据
jQuery使用ajax跨域请求获取数据 跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略, 即JavaScript或Cookie只能访问同域下的 ...
- 本地主机作服务器解决AJAX跨域请求访问数据的方法
近几天学到ajax,想测试一下ajax样例,由于之前在阿里租用的服务器过期了,于是想着让本地主机既做服务器又做客户端,只是简单地测试,应该还行. 于是,下载了xampp,下载网址http://www. ...
- AJAX跨域请求json数据的实现方法
这篇文章介绍了AJAX跨域请求json数据的实现方法,有需要的朋友可以参考一下 我们都知道,AJAX的一大限制是不允许跨域请求. 不过通过使用JSONP来实现.JSONP是一种通过脚本标记注入的方式, ...
- 关于ajax跨域请求API数据的一些问题
一般来说我们使用jquery的ajax来跨域请求API数据的时候每次请求,就只能请求一组数据,而且当我们再次点击发送ajax请求的时候,新请求的数据会覆盖掉原来的数据,那么如何每次在请求的数据的时候, ...
随机推荐
- 部署php程序报错解决---TP框架
1.TP框架中 出现如下错误: 访问网站 http://192.168.0.204/ STORAGE_WRITE_ERROR_:./Runtime/Cache/Home/51620d75af91aa7 ...
- Oracle 笔记(一)
Oracle基本理论 l 主流数据库 微软公司:SQL Server ACCESS IBM公司:DB2 infomix Oracle公司:Oracle MySQL(AB公司) 小型数据库 AC ...
- flutter详解路由栈(二)
前面,我们已经知道如何简单在路由栈中 push.pop 实例,然而,当遇到一些特殊的情况,这显然不能满足需求.学习 Android 的同学知道 Activity 的各种启动模式可以完成相应需求,Flu ...
- Gin框架中文文档
Gin 是一个 go 写的 web 框架,具有高性能的优点.官方地址:https://github.com/gin-gonic/gin 带目录请移步 http://xf.shuangdeyu.com/ ...
- C#动态调用带有SoapHeader验证的WebServices
http://blog.csdn.net/u012995964/article/details/54573143 本文记录C#中通过反射动态的调用带有SoapHeader验证的WebServices服 ...
- 开启sentry权限控制hue
参考: cloudera官方授权:包括webui, ldap,sentry https://www.cloudera.com/documentation/enterprise/6/6.2/topics ...
- CDH日常运维
1/ 作业有问题: 查log,没log再跑一次查log. 如果没有log的情况,比如hiveserver2挂了,查strr. 2/ 查集群名字 #看hdfs集群的名字,在cdh的hdfs配置中查:na ...
- Java编程思想读书笔记 第十章 内部类
非静态内部类作用: 最基本的作用:名字隐藏和组织代码 用例:内部类不访问外部类的元素时可以直接new.(bad style!) 用例:通过外部类的非静态方法返回内部类的引用,这样隐含了内部类对象和其对 ...
- jumpserver-1.4.8安装步骤
1. 组件说明 Jumpserver 为管理后台, 管理员可以通过 Web 页面进行资产管理.用户管理.资产授权等操作, 用户可以通过 Web 页面进行资产登录, 文件管理等操作 koko 为 SSH ...
- Java基础——Modifier类
转自:https://www.cnblogs.com/baiqiantao/p/7478523.html 反射 Reflect Modifier 修饰符工具类 在查看反射相关的Class.Fiel ...