I want to do error handling in my play scala web application.
My application talks to the data base to fetch some rows, it follows following flow.
- First call to db to fetch some data
- Use the data in first call to fetch other data from db
- Form a response using the data received from last two db calls.
Below is my pseudocode.
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
}
Every method in the comprehension above returns a future, the signature of these methods are as below.
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] {...}
How shall I do error/failure handling, in the following situation
- When callFuture1 fails to fetch data from database. I want to return a appropriate error response with error message. Since callFuture2 only gets executed after callFuture1. I dont want to execute callFuture2 if callFuture1 has failed/erred and would want to return error message immediately. (Same thing for callFuture2 and callFuture3)
--edit--
I am trying to return an appropriate Error Response from getResponse() method, when either of the callFuture fails and not proceed to subsequent futureCalls.
I tried the following, based on Peter Neyens answer, but gave me an runtime error..
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
}
Runtime error i get
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
You could use the
Future.recoverWith
function, to customize the exception if theFuture
failed.This will result in a slightly uglier for comprehension :
Note that you could also define your own exceptions to use instead of
Exception
, if you want to add more information than just an error message.If you don't want fine grained control to set a different error message depending on the
Throwable
in the failedFuture
(like withcallFuture1
), you could enrichFuture
using an implicit class to set a custom error message somewhat simpler:Which you could use like :
In both cases, using
errorMsg
orrecoverWith
directly, you still rely onFuture
, so if aFuture
fails the followingFutures
will not be executed and you can directly use the error message inside the failedFuture
.You didn't specify how you would like to handle the error messages. If for example you want to use the error message to create a different
Response
you could userecoverWith
orrecover
.Say
future1
,future2
andfuture3
throwThrowable
exceptions namedFuture1Exception
,Future2Exception
andFuture3Exception
, respectively. Then you can return appropriate errorResponse
fromgetResponse()
method as follows:According to documentation
Future.recover