问:调用Ok()从异步数据库调用发送http响应的正确位置在哪里?
我已经采用了非常基本的Scala Play框架教程play-scala-starter-example作为起点,并添加了一些额外的基本Controller / Service类,这些类利用ReactiveCouchbase进行数据库访问。
申请成功:
我是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
}
查看“//问题行”,在地图中调用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可能还没有完成?
最后,我尝试在onSuccess()函数中调用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))
.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的异步方式构建它的正确和恰当的方法是什么?
答案 0 :(得分:4)
Action.async
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: Cannot write an instance of Unit to HTTP response. Try to define a Writeable[Unit]
,因为您没有返回任何内容。这里预计会Result
。
此外,你应该处理几个期货的看涨期权。如果您不这样做,即使客户收到了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))
}