【问题标题】:Play Scala 2.4 linking asynchronous calls播放 Scala 2.4 链接异步调用
【发布时间】:2015-06-11 23:30:04
【问题描述】:

我试图弄清楚如何链接多个异步调用并返回结果。我目前正在尝试先异步用户数据,并异步更新用户数据并返回结果,但似乎它不起作用:( 我使用了 map { result => Ok(result)},但 play 仍然认为我正在返回一个对象。有什么帮助吗?

def updateUserData() = Action.async { implicit request =>
updateUserForm.bindFromRequest.fold(
errors => Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(errors, Option(""), Option("")))),
{
  case (userData) =>
    request.session.get("email") match {
      case Some(email) =>
        getUser(email, userData.curent_password) map { userCheck =>
          if (userCheck) {
            updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
              Ok("please")
            }
            //val e = updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map {result => Ok("")}

            // user is valid now update the user data

            // call removeAuth to log out

            // redirect to home
            ///Ok (updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result => result})
            //Redirect(routes.settings.index()).addingToSession("email" -> email)
          } else {
            BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option("")))
          }
        }
      }
  })
}

我遇到的主要问题是这部分。我认为这是一些语法的问题。有人可以帮忙吗? 谢谢

updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
  Ok("please")
}

【问题讨论】:

  • 你能分享getUserupdateUserOnService的方法签名(更具体地说是返回类型)吗?没有它,我们很难推断出我们头脑中的类型:)
  • def updateUserOnService(email: String, firstName: String, lastName: String, n_password: String) = { WS.url("localhost:9000/settings/updateUser") .post(Map("email" -> Seq( email), "firstName" -> Seq(firstName), "lastName" -> Seq(lastName), "n_password" -> Seq(n_password))) }
  • def getUser(email: String, password: String) = { WS.url("localhost:81/authenticateUser") .post(Map("email" -> Seq(email), "password" -> Seq(password))) .map(response => response.body.contains("true")) }
  • 对不起,我是个新手,所以我不知道如何编辑或重新格式化代码,以便你们更容易看到:(

标签: scala asynchronous playframework playframework-2.0


【解决方案1】:

问题在于您的类型,它们与所需的类型不匹配。

.fold 必须在两个分支(错误和成功分支)中产生 Future[Result]

在成功的表单绑定分支中你有这个:

case (userData) => ... // The ... must evaluate to Future[Result]

看看你的第一个操作,我们看到:

request.session.get("email") match {
  case Some(email) => ...
}

这里的一个大问题是None 案例没有得到处理! (但这不会导致类型不匹配)。有类似下面的东西可以解决这个问题:case None => Future.successful(BadRequest(...))

继续前进:在Some 中,您有以下内容:

getUser(email, userData.curent_password) map { userCheck =>
  if (userCheck) {
    updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
       Ok("please")
    }
  } else {
    BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option("")))
  }
}

这就是问题所在:

getUser 将返回 Future[X],当您映射它时,您将拥有 Future[Y],其中 Y 将是 userCheck => ... 的计算结果。

在这种情况下,类型完全混淆了,因为当你在真分支上执行if(usercheck) 时,你在假分支上得到Future[Result],你得到Result。所以类型不会在两个分支上对齐,这是一个大问题,编译器会从中推断出Any

要解决此问题,请在 false 分支中创建一个 future:Future.successful(BadRequest(....))

好的,现在我们解决了最内部的类型问题,让我们开始倒退。在里面我们有Future[Result],如果我们返回一级(在getUser()之前),那么我们将有Future[Future[Result]]。这又不是我们想要的,因为我们需要Future[Result]

解决这个问题的方法是flatMap 而不是map,因为flatMap 当您需要返回相同的容器类型时,它会将其展平。一个简单的例子来理解这一点:

Seq(1, 2, 3).flatMap(i => Seq(i, i))
// res0: Seq[Int] = List(1, 1, 2, 2, 3, 3)

Futures 的情况下:

import scala.concurrent.Future   
import scala.concurrent.ExecutionContext.Implicits.global
Future(1).flatMap(i => Future(i*2))
// res1: scala.concurrent.Future[Int] = [....]

所以我们看到我们没有双重嵌套,而只有一个 Future

回到您的示例,这将是我更新后的代码,效果会更好:

def updateUserData() = Action.async { implicit request =>
  updateUserForm.bindFromRequest.fold(
    errors => Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(errors, Option(""), Option("")))),
    {
      case (userData) =>
        request.session.get("email") match {
          case Some(email) =>
            getUser(email, userData.curent_password).flatMap { userCheck =>
              if (userCheck) {
                updateUserOnService(email, userData.f_name, userData.l_name, userData.new_password) map { result =>
                  Ok("please")
                }
              } else {
                Future.successful(BadRequest(views.html.authenticated.settings.settings_hero(updateUserForm.bindFromRequest.withGlobalError(Messages("error.login", email)), Option(""), Option(""))))
              }
            }
        case None => Future.successful(BadRequest) // FIXME: Implement as you wish
        }
    })
}

【讨论】:

  • 嘿,非常感谢!我不能为你投票,因为我没有足够的声誉:(它现在确实有效!我知道类型是问题:(
  • @ShawnYoon 如果您发现这回答了问题,请用绿色复选标记将其标记为已回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-25
  • 1970-01-01
  • 2013-10-28
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多