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

时间:2018-02-02 13:16:03

标签: scala asynchronous playframework couchbase

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

我已经采用了非常基本的Scala Play框架教程play-scala-starter-example作为起点,并添加了一些额外的基本Controller / Service类,这些类利用ReactiveCouchbase进行数据库访问。

申请成功:

  • 连接到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

}

查看“//问题行”,在地图中调用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的异步方式构建它的正确和恰当的方法是什么?

1 个答案:

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

}