Scala/Play:如何将异步数据库调用的 JSON 结果写入 http Ok

Scala/Play: how to write JSON result from async db call to http Ok

问:调用 Ok() 以从异步数据库调用发送 http 响应的正确位置在哪里?

我已经学习了非常基础的 Scala Play 框架教程play-scala-starter-example as a starting point and adding some additional basic Controller / Service classes that make use of ReactiveCouchbase 来访问数据库。

申请成功:

我是 Scala/Play 的新手,当异步数据库调用完成时,我无法找到使用 Ok() 成功将 JSON 写回 http 响应的正确方法。

控制器内部 class 是以下函数:

  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

处理未来的结果

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))  

}

你必须return一个Result(或Future[Result]

您收到错误 CouchbaseController.scala:30:19: Cannot write an instance of Unit to HTTP response. Try to define a Writeable[Unit] 因为您没有 return 任何东西。此处应为 Result

处​​理期货链

另外,你还要处理好几个Futures的调用。如果不这样做,即使客户端收到了 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))

}