问:调用 Ok() 从异步数据库调用发送 http 响应的正确位置在哪里?
我已经学习了非常基本的 Scala Play 框架教程 play-scala-starter-example作为起点,添加一些使用 ReactiveCouchbase 的附加基本 Controller /服务类用于数据库访问。
申请成功:
- 连接到 Couchbase
- 在 Couchbase 中存储 JSON 文档
- 从 Couchbase 检索存储的 JSON 文档
- 将 JSON 的内容记录到控制台
我是 Scala/Play 新手,无法找出在异步数据库调用完成时使用 Ok() 成功将 JSON 写回 http 响应的正确方法。
Controller 类内部有以下函数:
def storeAndRead() = Action {
testBucket
.insert[JsValue]("key1", Json.obj("message" -> "Hello World", "type" -> "doc"))
val res = testBucket
.get("key1")
.map(i => Json.toJson(i.get))
.map(j => Ok(j)) // PROBLEM LINE
}
查看“//PROBLEM LINE”,在映射内调用 Ok() 会导致编译错误:
CouchbaseController.scala:30:19: Cannot write an instance of Unit to HTTP response. Try to define a Writeable[Unit]
稍后调用 Ok(),失败并出现不同的编译错误:
def storeAndRead() = Action {
testBucket
.insert[JsValue]("key1", Json.obj("message" -> "Hello World", "type" -> "doc"))
val res = testBucket
.get("key1")
.map(i => Json.toJson(i.get))
Ok(res)
}
编译错误:
CouchbaseController.scala:35:7: Cannot write an instance of scala.concurrent.Future[play.api.libs.json.JsValue] to HTTP response. Try to define a Writeable[scala.concurrent.Future[play.api.libs.json.JsValue]]
在第二种情况下,我认为问题在于调用 Ok() 时 Future 可能尚未完成?
最后,我尝试将对 Ok() 的调用放在 onSuccess() 函数中,以确保在异步函数完成后调用它:
def storeAndRead() = Action {
testBucket
.insert[JsValue]("key1", Json.obj("message" -> "Hello World", "type" -> "doc"))
val res = testBucket
.get("key1")
.map(i => Json.toJson(i.get))
.onSuccess {
//case doc => Console.println("completed: " + doc)
case doc => Ok(doc)
}
}
再次...编译错误:
CouchbaseController.scala:22:24: overloaded method value apply with alternatives:
[error] (block: => play.api.mvc.Result)play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error] (block: play.api.mvc.Request[play.api.mvc.AnyContent] => play.api.mvc.Result)play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error] [A](bodyParser: play.api.mvc.BodyParser[A])play.api.mvc.ActionBuilder[play.api.mvc.Request,A]
[error] cannot be applied to (Unit)
[error] def storeAndRead() = Action {
问题:
我显然错过了一些相当基本的东西:
这种基本场景下应该在哪里调用Ok()呢?我认为异步数据库请求完成时需要作为回调结果调用它?
对于 Scala/Play 以异步方式构建此结构的正确且适当的方法是什么?
最佳答案
使用 Action.async
处理 future 结果
Play 知道如何处理 Future
(异步调用)。您必须使用 Action.async
。
例如:
def myAction = Action.async {
// ...
myFuture.map(resp => Ok(Json.toJson(resp)))
}
就您而言:
def storeAndRead() = Action.async {
// by the way, the expression behind probably returns a future, you should handle it
testBucket
.insert[JsValue]("key1", Json.obj("message" -> "Hello World", "type" -> "doc"))
testBucket
.get("key1")
.map(i => Json.toJson(i.get))
.map(j => Ok(j))
}
您必须返回一个Result
(或一个Future[Result]
)
您收到错误CouchbaseController.scala:30:19:无法将 Unit 实例写入 HTTP 响应。尝试定义一个 Writeable[Unit]
因为您不返回任何内容。这里需要一个 Result
。
处理 future 链
此外,您应该处理多个 Future 的调用。如果不这样做,即使客户端收到了 http 响应,您也会收到静默错误。
例如:
def storeAndRead() = Action.async {
for {
_ <- testBucket.insert[JsValue]("key1", Json.obj("message" -> "Hello World", "type" -> "doc"))
value <- testBucket.get("key1")
} yield Ok(Json.toJson(value))
}
关于斯卡拉/Play : how to write JSON result from async db call to http Ok,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48583515/