zoukankan      html  css  js  c++  java
  • 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版)

  • 相关阅读:
    什么样的代码称得上是好代码?
    九年程序人生 总结分享
    Docker入门 第一课 --.Net Core 使用Docker全程记录
    阿里云 Windows Server 2012 r2 部署asp.net mvc网站 平坑之旅
    Visual studio 2015 Community 安装过程中遇到问题的终极解决
    Activiti6.0 spring5 工作流引擎 java SSM流程审批 项目框架
    java 进销存 库存管理 销售报表 商户管理 springmvc SSM crm 项目
    Leetcode名企之路
    24. 两两交换链表中的节点
    21. 合并两个有序链表
  • 原文地址:https://www.cnblogs.com/guxia/p/6385992.html
Copyright © 2011-2022 走看看