Play框架处理会话状态

10
我有一个基于Play框架和Scala构建的Webapp,旨在向用户提供一组问题及每个问题的答案。其中一些问题的答案是单选按钮类型,而另一些则是复选框类型。当用户点击"开始测试"时,我会调用控制器,获取问题列表及其答案,并将结果作为一个case类返回给视图模板。现在,我需要维护测试的状态,因为用户回答每个问题。他可以前往上一个或下一个问题,因此我需要跟踪他回答的所有问题。
从Java EE背景出发,我认为可以将case类存储在会话中并在控制器中进行操作。但不幸的是,Play框架的会话是一个键值对的String、String,而不是String、Object。由于我的Play框架经验有限,我现在陷入了困境,希望听到您的建议。
3个回答

11

Play框架中并没有状态,所以如果你想在多��HTTP请求之间保留一些数据,则可以使用Session范围。它实际上创建了带有键/值对(字符串,字符串)的cookie,并且它们的大小限制为4KB。

我的建议是使用Json,Play-json库非常棒。如果您有带有JSON 读/写/格式化 组合器的模型,那么很简单。

Ok(render(Questions)).withSession("answers" -> Json.prettyPrint(Json.toJson(Answer)))

读取会话值可以像这样完成:

def index = Action { implicit request =>
      session.get("answers").map { answers =>
        val jsValueAnswers: JsValue = Json.parse(answers)
        val answersModel: YourAnswerModel = Json.fromJson(jsValueAnswers) 
        Ok("Got previous answers and created session cookie with them")
        .withSession("answers2" -> Json.prettyPrint(Json.toJson(answersModel)))
      }
    }

希望这能对你有所帮助。

干杯!


问题在于我需要将整个测试存储在会话中,而不仅仅是用户看到的当前问题的答案。用户看到的问题有一组答案,他将选择其中一个并提交给服务器。然后,我应该采取他对该问题的答案,并针对该问题更新我的Test对象。我只是想知道如何利用Json来做到这一点。您能否详细说明一下,因为仅存储将呈现的问题的当前答案对我来说还不够! - joesan
一切都取决于您对象的模型,您可以编写自己的对象读取/写入/格式化程序,也可以使用宏嵌套。case class Answer(id:Long, value:String) case class TestObject(id: Long, List(Answers)) implicit val answerRead = Json.read[Answer] implicit val answerWrite = Json.write[Answer] - B. Pesevski
之前留言有些混乱:) 我是新来的 :)那么,在你的情况下,将会话值从字符串转换为JSON,然后在每个POST/GET中将其转换为对象并更新该值,我认为不是一个好的解决方案。因此,我建议您采用James的建议并使用memcached。 - B. Pesevski

9

Play只支持字符串类型的会话,因为它将所有会话状态存储在cookie中 - 这意味着Play节点可以扩展而无需任何形式的集群/状态共享技术。

如果您要存储的数据很小(小于2k),那么只需将其序列化为JSON,或者像您的情况一样,更简单的方法是将其序列化为逗号分隔的答案列表。

否则,您可以将其存储在如memcached或redis等缓存中。


Memcached算是一种数据库吗?我从未使用过它。从名称上看,它似乎是一种缓存,我可以用它来存储键值对,模拟会话并将我的对象存储在那里。它就是为这个目的而设计的吗?我会进一步探索。 - joesan

4

Play框架旨在设计成无状态的,因此您不会找到任何具体的服务器端会话概念。当然,如果您需要它,比如在这种情况下,您可以使用服务器端技术(缓存或数据库)作为容器来存储您的项,使用Play-cookie session来存储访问密钥字符串。

这是使用Play Cache存储对象的示例:

import play.api.cache.Cache
import play.api.Play.current

val key = UUID.randomUUID.toString
Cache.set(key, yourModel, 0)
Ok.withSession("answer_key" -> key)

这是一个检索的示例:

import play.api.cache.Cache
import play.api.Play.current

val key = session.get("answer_key").getOrElse("none")
val yourModel = Cache.getAs[ModelClass](key).getOrElse(//whatever you want if not exists)

1
Cache的问题在于,即使为对象在缓存中存活的时间指定了超时时间,也不能保证数据仍然存在。因此,我不想信任Cache。 - joesan
我不认为这是一个真正的问题。Play使用EHCache作为默认实现,它实现了一个持久化磁盘存储器,可以在虚拟机重启时存储数据。类似的行为也存在于JEE Web会话中。 使用cookie,您的序列化数据受到4 Kb的限制,而使用缓存则没有这个限制。 - Marco
1
你为什么认为这不是一个真正的问题?Play缓存机制背后的想法是检索经常请求的对象,而不是用来替代会话。请查看链接:http://www.playframework.com/documentation/2.0.3/JavaCache 第一段明确指出,缓存中的数据可能会丢失!就因为这个原因,我不想相信它。 - joesan
我认为在你的情况下这不是一个真正的问题。但我理解你的论点,你可以使用数据库和缓存来进行优化。当然,作为 cookie 的替代方案(cookie 不适合作为数据容器)。 - Marco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接