在处理完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 跨重定向请求传递数据的更多相关文章

  1. Spring 梳理-跨重定向请求传递数据-Flash

    Spring MVC Flash Attribute 的讲解与使用示例 1. Spring MVC 3.1版本加了一个很有用的特性,Flash属性,它能解决一个长久以来缺少解决的问题,一个POST/R ...

  2. Spring之跨重定向请求传递数据

    摘要 在开发场景中,大部分数据都是使用请求转发(forward)进行传递,而使用重定向(redirect)传递数据可能比较少. 那么问题来了:请求中的数据生命周期存活时间只在一个请求转发(reques ...

  3. SpringMVC跨重定向请求传递数据

    (1)使用URL模板以路径变量和查询参数的形式传递数据(一些简单的数据) @GetMapping("/home/index") public String index(Model ...

  4. spring跨重定向传递数据

    spring跨重定向传递数据 为何要重定向? 作用之一:防止表单重复提交 如何重定向? // 在控制器方法返回的视图名称中,以redirect:开头的String不是用来查找视图的,而是用来指导浏览器 ...

  5. Jquery跨域请求php数据(jsonp)

    Jquery跨域请求php数据 我们一般用到ajax的时候是在同服务器下,一般情况下不会跨域,但有时候需要调用其他域名或ip下的数据的时候,遇到跨域请求数据的时候. 今天在工作中碰到javascrip ...

  6. jQuery使用ajax跨域请求获取数据

    jQuery使用ajax跨域请求获取数据  跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略, 即JavaScript或Cookie只能访问同域下的 ...

  7. 本地主机作服务器解决AJAX跨域请求访问数据的方法

    近几天学到ajax,想测试一下ajax样例,由于之前在阿里租用的服务器过期了,于是想着让本地主机既做服务器又做客户端,只是简单地测试,应该还行. 于是,下载了xampp,下载网址http://www. ...

  8. AJAX跨域请求json数据的实现方法

    这篇文章介绍了AJAX跨域请求json数据的实现方法,有需要的朋友可以参考一下 我们都知道,AJAX的一大限制是不允许跨域请求. 不过通过使用JSONP来实现.JSONP是一种通过脚本标记注入的方式, ...

  9. 关于ajax跨域请求API数据的一些问题

    一般来说我们使用jquery的ajax来跨域请求API数据的时候每次请求,就只能请求一组数据,而且当我们再次点击发送ajax请求的时候,新请求的数据会覆盖掉原来的数据,那么如何每次在请求的数据的时候, ...

随机推荐

  1. windows 快捷键 部分

    1.快速启动任务栏锁定的任务 WIN+任务栏任务顺序(左侧开始数) 2.运行 WIN+R mstsc--->远程桌面链接 regedit--->注册表信息 services.msc---& ...

  2. SI_WorkShop_V4安装手册

    V4安装手册 第一步 启动workshopV4 解压workshopV4.rar,在解压后的目录中双击eclipse.exe启动workshopV4,启动画面如下图所示:(注:解压后第1次启动速度会慢 ...

  3. Spring MVC -- 去掉静态资源的拦截

    <!-- springmvc的前端控制器 --> <servlet> <servlet-name>springMVC</servlet-name> &l ...

  4. Django的Mov逻辑的管理特色

    Django的MOV逻辑的管理特色 首先我们谈论到一个逻辑上的概念都从它的起点说起,在我看来mov的起点肯定就是Model了,那么Model有什莫特色呢 如果一个项目定义的Django那么Django ...

  5. centos8飞行驾驶舱和docker安装

    零.先解决cenos8的网络(systemctl restart network.service已被废弃) 1.# vim /etc/sysconfig/network-scripts/ifcfg-e ...

  6. tomcat7远程代码执行 ImageMagick 命令执行漏洞

    tomcat7远程代码执行 windows     / linux   ::$DATA ImageMagick 命令执行漏洞(CVE-2016–3714) base64编码

  7. redis学习(三)

    如何保障reids的数据安全和性能?   一.持久化选项 1.快照snapshotting 它可以将存在于某一时刻的所有数据都写入硬盘里面. 配置选项示例: save 60 1000 注:从最近一次创 ...

  8. css实现毛玻璃效果

    css实现毛玻璃效果,效果图 1,html代码 <div class="mainHolder"> <div class="textHolder" ...

  9. Maven从入门到精通(二)

    上一篇我们讲解了Maven项目的基本目录结构,也已经安装了Maven的开发环境,接下来我们要重点讲解一下Maven最核心的灵魂pom.xml文件 POM:Project Object Model 项目 ...

  10. poj2352(树状数组)

    题目链接:https://vjudge.net/problem/POJ-2352 题意:在直角坐标系中给出n个点的 (x,y),(0<=x,y<=32000),定义每个点的level为(x ...