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

Ral*_*lph 7 monads scala playframework

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

POST /users/:userId/items
Run Code Online (Sandbox Code Playgroud)

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

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

我正在尝试使用for以下方法撰写:

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

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

我不确定for结构后要放什么.我试过了getOrElse,但是没有编译,因为Future没有getOrElse方法.

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

PUT /users/:userId/foo/:fooId/bar/:barId
Run Code Online (Sandbox Code Playgroud)

并确认userId,fooIdbarId正在做的更新之前都有效.所有这些电话的(Users.findById,Foo.findById,和Bar.findById)将返回Future[Option[A]].

Kev*_*ght 9

它是双嵌套(FutureOption),似乎每一次抢人.如果你能先把东西弄平,事情会变得容易多了.

在这种情况下,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) }
Run Code Online (Sandbox Code Playgroud)

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

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

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

除了NotFoundException仍然会暴露的任何例外情况.