我想在我的scala Web应用程序中进行错误处理。
我的应用程序与数据库进行对话以获取一些行,它遵循以下流程。
下面是我的伪代码。
def getResponse(name: String)
(implicit ctxt: ExecutionContext): Future[Response] = {
for {
future1 <- callFuture1(name)
future2 <- callFuture2(future1.data)
future3 <- callFuture3(future1.data, future2.data)
} yield future3
}
上面理解中的每个方法都会返回一个 future ,这些方法的签名如下。
private def callFuture1(name: String)
(implicit ctxt: ExecutionContext): Future[SomeType1] {...}
private def callFuture2(keywords: List[String])
(implicit ctxt: ExecutionContext): Future[SomeType2] {...}
private def callFuture3(data: List[SomeType3], counts: List[Int])
(implicit ctxt: ExecutionContext): Future[Response] {...}
在以下情况下应如何处理错误/故障
带有错误消息的适当错误响应。从callFuture2开始
仅在callFuture1之后执行。我不想执行
如果callFuture1失败/出错并想返回,则调用callFuture2
立即出现错误消息。 (对于callFuture2和
callFuture3)
- 编辑 -
当任何一个callFuture失败并且不继续进行后续的futureCalls时,我试图从getResponse()方法返回适当的错误响应。
我根据Peter Neyens的答案尝试了以下操作,但给了我运行时错误。
def getResponse(name: String)
(implicit ctxt: ExecutionContext): Future[Response] = {
for {
future1 <- callFuture1(name) recoverWith {
case e:Exception => return Future{Response(Nil,Nil,e.getMessage)}
}
future2 <- callFuture2(future1.data)
future3 <- callFuture3(future1.data, future2.data)
} yield future3
}
我得到的运行时错误
ERROR] [08/31/2015 02:09:45.011] [play-akka.actor.default-dispatcher-3] [ActorSystem(play)] Uncaught error from thread [play-akka.actor.default-dispatcher-3] (scala.runtime.NonLocalReturnControl)
[error] a.a.ActorSystemImpl - Uncaught error from thread [play-akka.actor.default-dispatcher-3]
scala.runtime.NonLocalReturnControl: null
最佳答案
如果Future.recoverWith
失败,则可以使用 Future
函数来自定义异常。
val failed = Future.failed(new Exception("boom"))
failed recoverWith {
case e: Exception => Future.failed(new Exception("A prettier error message", e)
}
这将导致理解力稍差一些:
for {
future1 <- callFuture1(name) recoverWith {
case npe: NullPointerException =>
Future.failed(new Exception("how did this happen in Scala ?", npe))
case e: IllegalArgumentException =>
Future.failed(new Exception("better watch what you give me", e))
case t: Throwable =>
Future.failed(new Exception("pretty message A", t))
}
future2 <- callFuture2(future1.data) recoverWith {
case e: Exception => Future.failed(new Exception("pretty message B", e))
}
future3 <- callFuture3(future1.data, future2.data) recoverWith {
case e: Exception => Future.failed(new Exception("pretty message C", e))
}
} yield future3
请注意,如果您想添加除错误消息之外的更多信息,还可以定义自己的异常来代替
Exception
。如果您不希望细粒度的控件根据失败的
Throwable
中的Future
设置不同的错误消息(例如callFuture1
),则可以使用隐式类来丰富Future
,以将自定义错误消息设置得更为简单:implicit class ErrorMessageFuture[A](val future: Future[A]) extends AnyVal {
def errorMsg(error: String): Future[A] = future.recoverWith {
case t: Throwable => Future.failed(new Exception(error, t))
}
}
你可以这样使用:
for {
future1 <- callFuture1(name) errorMsg "pretty A"
future2 <- callFuture2(future1.data) errorMsg "pretty B"
future3 <- callFuture3(future1.data, future2.data) errorMsg "pretty C"
} yield future3
在这两种情况下,直接使用
errorMsg
或recoverWith
,您仍然依赖Future
,因此,如果Future
失败,则不会执行以下Futures
,您可以直接在失败的Future
中使用错误消息。您没有指定如何处理错误消息。例如,如果您要使用错误消息来创建其他
Response
,则可以使用recoverWith
或recover
。future3 recover { case e: Exception =>
val errorMsg = e.getMessage
InternalServerError(errorMsg)
}