scala error-handling playframework future

Error al manejar Scala: Futuro para la comprensión



error-handling playframework (2)

Diga future1 , future2 y future3 lanzando excepciones Future1Exception llamadas Future1Exception , Future2Exception y Future3Exception , respectivamente. Luego puede devolver la Response error apropiada del método getResponse() siguiente manera:

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).recover { case e: Future1Exception => // build appropriate Response(...) case e: Future2Exception => // build appropriate Response(...) case e: Future3Exception => // build appropriate Response(...) } }

Según documentación Future.recover

Crea un nuevo futuro que manejará cualquier tirable coincidente que este futuro pueda contener.

Quiero hacer el manejo de errores en mi aplicación web Play Scala.

Mi aplicación habla con la base de datos para obtener algunas filas, sigue el siguiente flujo.

  1. Primera llamada a db para buscar algunos datos.
  2. Utilice los datos de la primera llamada para obtener otros datos de db
  3. Forme una respuesta utilizando los datos recibidos de las últimas dos llamadas de db.

A continuación se muestra mi pseudocódigo.

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 }

Cada método en la comprensión anterior devuelve un futuro, la firma de estos métodos es la siguiente.

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] {...}

¿Cómo debo hacer el manejo de errores / fallas, en la siguiente situación?

  • Cuando callFuture1 no puede recuperar datos de la base de datos. Quiero devolver una respuesta de error apropiada con un mensaje de error. Dado que callFuture2 solo se ejecuta después de callFuture1. No deseo ejecutar callFuture2 si callFuture1 ha fallado / ha fallado y quisiera devolver un mensaje de error inmediatamente. (Lo mismo para callFuture2 y callFuture3)

--editar--

Estoy tratando de devolver una respuesta de error apropiada del método getResponse (), cuando cualquiera de los dos CallFuture falla y no se pasa a las siguientes llamadas futuras.

Intenté lo siguiente, basado en la respuesta de Peter Neyens, pero me dio un error de tiempo de ejecución ...

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 de tiempo de ejecución me sale

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


Puede usar la función Future.recoverWith para personalizar la excepción si el Future falla.

val failed = Future.failed(new Exception("boom")) failed recoverWith { case e: Exception => Future.failed(new Exception("A prettier error message", e) }

Esto resultará en un poco más feo para la comprensión:

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

Tenga en cuenta que también puede definir sus propias excepciones para usar en lugar de Exception , si desea agregar más información que solo un mensaje de error.

Si no desea que el control detallado establezca un mensaje de error diferente según el Throwable en el Future fallido (como con callFuture1 ), puede enriquecer el Future usando una clase implícita para establecer un mensaje de error personalizado algo más simple:

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)) } }

Que puedes usar como:

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

En ambos casos, utilizando errorMsg o recoverWith directamente, aún depende de Future , por lo que si falla un Future los siguientes Futures no se ejecutarán y puede usar directamente el mensaje de error dentro del Future fallido.

No especificó cómo le gustaría manejar los mensajes de error. Si, por ejemplo, desea utilizar el mensaje de error para crear una Response diferente, puede usar recoverWith o recoverWith .

future3 recover { case e: Exception => val errorMsg = e.getMessage InternalServerError(errorMsg) }