我正在使用Scala,Play Framework 2.1.x和reactivemongo驱动程序。
我有一个api电话:
def getStuff(userId: String) = Action(implicit request => {
Async {
UserDao().getStuffOf(userId = userId).toList() map {
stuffLst => Ok(stuffLst)
}
}
})
它在99%的时间内工作正常但有时可能会失败(无关紧要,这不是问题)。
我希望在出现错误的情况下恢复,所以我补充说:
recover { case _ => BadRequest("")}
但这并没有让我从错误中恢复过来 我在scala控制台上尝试了相同的概念并且工作正常:
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
var f = future { throw new Exception("") } map {_ => 2} recover { case _ => 1}
Await.result(f, 1 nanos)
按预期返回1 我目前用Async包装了Async:
try{
Async {...}
} catch {
case _ => BadRequest("")
}
这会抓住错误。
我在网上浏览了一些Scala的未来文档,我很困惑为什么恢复对我不起作用。
有谁知道为什么?我会错过什么来解决它?
答案 0 :(得分:41)
为什么它失败实际上是100%重要的。如果我们将代码分布在多行代码中,您就会明白原因:
def getStuff(userId: String) = Action(implicit request => {
Async {
val future = UserDao().getStuffOf(userId = userId).toList()
val mappedFuture = future.map {
stuffLst => Ok(stuffLst)
}
mappedFuture.recover { case _ => BadRequest("")}
}
})
所以,UserDao().getStuffOf(userId = userId).toList()
会给你一个未来。未来代表着可能尚未发生的事情。如果该事件抛出异常,您可以在恢复中处理该异常。但是,在您的情况下,错误发生在甚至在创建未来之前,UserDao().getStuffOf(userId = userId).toList()
调用正在抛出异常,而不是返回未来。因此,恢复未来的呼吁永远不会被执行。它相当于在Scala repl中执行此操作:
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
var f = { throw new Exception(""); future { "foo" } map {_ => 2} recover { case _ => 1} }
Await.result(f, 1 nanos) }
显然这不起作用,因为你从来没有创造过未来,因为在创建未来的代码发生之前抛出了异常。
所以解决方案是在try catch块中包含对UserDao().getStuffOf(userId = userId).toList()
的调用,或者找出它为什么在你调用的方法中失败,并在那里捕获异常,并返回失败的未来。
答案 1 :(得分:4)
如果你有更高版本的Play,例如2.2.x,你可以这样做:
def urlTest() = Action.async {
val holder: WSRequestHolder = WS.url("www.idontexist.io")
holder.get.map {
response =>
println("Yay, I worked")
Ok
}.recover {
case _ =>
Log.error("Oops, not gonna happen")
InternalServerError("Failure")
}
}