使用Scala在Play Framework中创建“Future”结果

时间:2014-01-28 18:15:41

标签: scala playframework monads

我正在尝试为以下URL编写Play Framework异步Action:

POST /users/:userId/items

我的数据库调用所有返回Future[...],其中... Option[A]find方法,Option[Id]为创建方法。

我想在尝试创建新项目之前检查userId是否存在。我有一个方法Users.findById(userId),它返回Future[Option[User]]。如果用户存在,则结果为Some(User),如果不存在,则结果为NoneItems.create()也会返回Future[Option[itemId]]

我正在尝试使用for撰写内容:

for {
  user <- Users.findById(userId)
  if user.isDefined
} yield {
  Items.create(...) map { itemId => Ok(itemId) } getOrElse NotFound
}

如果项目成功创建,我想返回Ok(itemId)。我不知道如何处理错误案例。如果userId无效或无法创建项目(可能是某个字段与数据库中已有的唯一值冲突),我想返回NotFound

我不确定在for结构之后放什么。我尝试了getOrElse,但由于Future没有getOrElse方法,因此无法编译。

理想情况下,我可以处理包含多个要检查的ID的URL,例如:

PUT /users/:userId/foo/:fooId/bar/:barId

并确认userIdfooIdbarId在进行更新前均有效。所有这些来电(Users.findByIdFoo.findByIdBar.findById)都会返回Future[Option[A]]

1 个答案:

答案 0 :(得分:9)

双重嵌套(Future Option)似乎每次都会吸引人们。如果你能先把东西弄平,事情会变得容易多了。

在这种情况下,Future已经有了表示错误条件的方法,它可以包装Exception以及成功值,这是你可以使用的......

// making this a Singleton avoids the cost of building a stack trace,
// which only happens when an Exception is constructed (not when it's thrown)
object NotFoundException extends RuntimeException("Empty Option")

// The map operation will trap any thrown exception and fail the Future
def squish[T](x: Future[Option[T]]) =
  x map { _.getOrElse(throw NotFoundException) }

现在,在理解中使用这些压扁的结果要容易得多:

val result = for {
  user <- squish(Users findById userId)
  itemId <- squish(Items.create(user, ...))
} yield {
  Ok(itemId)
} recover {
  case NotFoundException => NotFound
}

当然,这将评估未来。毕竟这是异步编程:)

NotFoundException以外的任何例外情况仍会曝光。