我正在尝试为以下URL编写Play Framework异步Action:
POST /users/:userId/items
我的数据库调用所有返回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
}
如果项目成功创建,我想返回Ok(itemId)
。我不知道如何处理错误案例。如果userId无效或无法创建项目(可能是某个字段与数据库中已有的唯一值冲突),我想返回NotFound
。
我不确定在for
结构之后放什么。我尝试了getOrElse
,但由于Future
没有getOrElse
方法,因此无法编译。
理想情况下,我可以处理包含多个要检查的ID的URL,例如:
PUT /users/:userId/foo/:fooId/bar/:barId
并确认userId
,fooId
和barId
在进行更新前均有效。所有这些来电(Users.findById
,Foo.findById
和Bar.findById
)都会返回Future[Option[A]]
。
答案 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
以外的任何例外情况仍会曝光。