Play 2.0 中文资料 - 会话和 Flash 域

会话和 Flash 域在 Play 中有何不同


如果你不得不要跨多个 HTTP 请求来保存数据, 你可以存在会话或是 Flash 域中. 存储在会话中的数据在整个会话期间可用, 而存储在 Flash 域中数据只对下一次请求有效.

理解到会话和 Flash 数据不是由服务器来存储,而是以 Cookie 机制添加到每一次后续的 HTTP 请求是很重要的. 这也就意味着数据的大小是很受限的 (不到 4 KB) ,而且你只能存储字符串.

当然了, Cookie 值是由密钥签名了的,这使得客户端不存修改 Cookie 数据(或者说这样做会让它失效).

Play 会话不能当成缓存来用.  假如你需要缓存与某一会话相关的数据, 你可以使用 Play 内置的缓存机制并存储一个唯一的 ID 在用户会话中与之关联.
会话没有技术上的超时控制. 它在用户关闭 Web 浏览器后就会过期. 如果你的应用需要一种功能性的超时控制, 那就在用户会话中存储一个时间戳,并在应用需要的时候用它 (例如: 最大的会话持续时间, 最大的非活动时间, 等等.).
读取会话值

你可以从 HTTP 请求中获取传入的会话:
1def index = Action { request =>
2  request.session.get("connected").map { user =>
3    Ok("Hello " + user)
4  }.getOrElse {
5    Unauthorized("Oops, you are not connected")
6  }
7}

另一种方式是,你能从请求中隐式的获取会话:
1def index = Action { implicit request =>
2  session.get("connected").map { user =>
3    Ok("Hello " + user)
4  }.getOrElse {
5    Unauthorized("Oops, you are not connected")
6  }
7}

Unmi 注: request.session 的类型是  Map[String, String],其中存放的就是一个个 Session 键值对,而 Map 的 get(key:String) 方法返回的是 Option[String],即 Some(value) 或是 None 值(key不存在时取得的值),Some(value).map() 方法能处理期中的 value 值。.getOrElse() 方法是在 session.get("connected") 为 None 时执行的操作。

看一下 Play 存储的会话在 Cookie 中的样子:


可以看到存储 Cookie 时用的 Key 是 “PLAY_SESSION"。值用了密钥签名了,随意修改了值也是无效的。

存储数据到会话中

由于 Session 是个 Cookie, 也是个 HTTP 头,所以你可用操作其他 Result 属性那样的方式操作 Session 数据:
1Ok("Welcome!").withSession(
2  "connected" -> "user@gmail.com"
3)

Unmi 注: 上面的方法调用的是 Results.scala 中的 def withSession(session: (String, String)*): A 方法。

注: 这会替换掉整个 Session. 假如你需要添加元素到已有的 Session 中, 方法是先添加元素到传入的 Session 中, 接着把它作为新的Session:
1Ok("Hello World!").withSession(
2  session + ("saidHello" -> "yes")
3)

你可以用同样的方式从传入的 Session 中移除任何值:
1Ok("Theme reset!").withSession(
2  session - "theme"
3)

Unmi 注: 上面两段代码调用的是 Results.scala  中的 def withSession(session: Session): A 方法。因为 session 是 Map[String, String] 类型,所以可以用 +/- 号来增减元素。

废弃整个 Session

有一个特殊的操作用来废弃整个 Session:
1Ok("Bye").withNewSession

Flash 域

Flash 域工作方式非常像 Session, 但有两点不同:
  • 数据仅为一次请求而保留
  • Flash 的 Cookie 未被签名, 这留给了用户修改它的可能性.

重点: Flash 域仅能用于简单的非 Ajax 应用中在传送 成功/错误 的消息. 由于数据只保存给下一次请求,而且在复杂的 Web 应用中无法保证请求的顺序,所以在竞争条件下 Flash 可能不那么好使.
下面是使用 Flash 域的例子:
 1def index = Action { implicit request =>
 2  Ok {
 3    flash.get("success").getOrElse("Welcome!")
 4  }
 5}
 6
 7def save = Action {
 8  Redirect("/home").flashing(
 9    "success" -> "The item has been created"
10  )
11}

Unmi 注: Flash 域的数据可在服务端重定向到的下一个请求中可见。再来看一个 Flash 域的 Cookie 发生了什么,因为使用了 Redirect 转向,所以用浏览器捕捉不到这时候的 Cookie 值,但可以用 curl -v 命令来查看,见下图:


上图中可以见到当转向到下个请求 /home 时用的 Cookie 的 Key 是 ”PLAY_FLASH“,并且值未被签名。 永久链接 https://yanbin.blog/play2-0-tutorials-cn-session-flash-scope/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。