2012-06-05 14 views
10

Mi código de acción del controlador se ve así:Cómo manejar excepciones en un bloque playframework 2 asíncrono (Scala)

def addIngredient() = Action { implicit request => 
    val boundForm = ingredientForm.bindFromRequest 
    boundForm.fold(
     formWithErrors => BadRequest(views.html.Admin.index(formWithErrors)), 
     value => { 
     Async { 
      val created = Service.addIngredient(value.name, value.description) 
      created map { ingredient => 
      Redirect(routes.Admin.index()).flashing("success" -> "Ingredient '%s' added".format(ingredient.name)) 
      } 

      // TODO on exception do the following 
      // BadRequest(views.html.Admin.index(boundForm.copy(errors = Seq(FormError("", ex.getMessage()))))) 
     } 
     }) 
    } 

Mi Service.addIngredient (...) devuelve una promesa [ingrediente], pero también puede lanzar una ValidationException personalizada. Cuando se lanza esta excepción, me gustaría devolver el código comentado.

Actualmente la página se representa como 500 y en los registros que tengo:

play - Waiting for a promise, but got an error: Ingredient with name 'test' already exists. services.ValidationException: Ingredient with name 'test' already exists.

Dos preguntas:

  • ¿Es una mala idea para devolver esta excepción de mi servicio, hay una mejor/más forma scala para manejar este caso?
  • ¿Cómo atrapo la excepción?
+0

Hubo un error que se ha solucionado hace unos días. Ver [este compromiso] (https://github.com/playframework/Play20/commit/def888333ea435437edb7f70ca3b7f48877af1c7). Puede manejar excepciones de tiempo de ejecución en el gancho 'onError' de su objeto' Global'. –

+0

pero no hay forma de detectar la excepción localmente? – Somatik

+0

Sí, puedes verlo como cualquier otra excepción, como se muestra en la respuesta del kheraud. –

Respuesta

2

diría de una manera funcional pura habría sido la utilización de un tipo que puede contener estados válidos y de error.

Para ello, puede utilizar el formulario de Validation scalaz

Pero si no necesitan más que lo del scalaz (se le ^^), se puede utilizar un material muy sencilla utilizando un Promise[Either[String, Ingredient]] como el resultado y su fold método en el bloque Async. Es decir, map para convertir el valor cuando se canjea la promesa y fold en lo que se canjea. cheque

El punto bueno => no es una excepción => todo lo que se escribe :-)

EDITAR

Se podría necesitar un poco de información más, aquí están las dos opciones: intentar coger, gracias a @kheraud) y O bien. No puse el Validation, pregúnteme si es necesario. objeto Aplicación extends Controller {

def index = Action { 
    Ok(views.html.index("Your new application is ready.")) 
    } 

    //Using Try Catch 
    // What was missing was the wrapping of the BadRequest into a Promise since the Async 
    // is requiring such result. That's done using Promise.pure 
    def test1 = Async { 
    try { 
     val created = Promise.pure(new {val name:String = "myname"}) 
     created map { stuff => 
     Redirect(routes.Application.index()).flashing("success" -> "Stuff '%s' show".format(stuff.name)) 
     } 
    } catch { 
     case _ => { 
     Promise.pure(Redirect(routes.Application.index()).flashing("error" -> "an error occurred man")) 
     } 
    } 
    } 


    //Using Either (kind of Validation) 
    // on the Left side => a success value with a name 
    val success = Left(new {val name:String = "myname"}) 
    // on the Right side the exception message (could be an Exception instance however => to keep the stack) 
    val fail = Right("Bang bang!") 

    // How to use that 
    // I simulate your service using Promise.pure that wraps the Either result 
    // so the return type of service should be Promise[Either[{val name:String}, String]] in this exemple 
    // Then while mapping (that is create a Promise around the convert content), we folds to create the right Result (Redirect in this case). 
    // the good point => completely compiled time checked ! and no wrapping with pure for the error case. 
    def test2(trySuccess:Boolean) = Async { 
     val created = Promise.pure(if (trySuccess) success else fail) 
     created map { stuff /* the either */ => 
     stuff.fold(
      /*success case*/s => Redirect(routes.Application.index()).flashing("success" -> "Stuff '%s' show".format(s.name)), 
      /*the error case*/f => Redirect(routes.Application.index()).flashing("error" -> f) 
     ) 

     } 

    } 

} 
+1

La solución tipo revisada es claramente mejor. Gracias por los ejemplos! – iwalktheline

+0

Sin problema Aquí para eso ^^. Cheers –

+0

La solución try-catch no está funcionando en Play 2.0.1, ¿podría estar relacionada con el error/parche al que se refería Julien? – Somatik

0

¿No puedes simplemente capturar la excepción en tu bloque Async?

Async { 
    try { 
     val created = Service.addIngredient(value.name, value.description) 
     created map { ingredient => 
      Redirect(routes.Admin.index()).flashing("success" -> "Ingredient '%s' added".format(ingredient.name)) 
     } 
    } catch { 
     case _ => { 
      Promise.pure(Redirect(routes.Admin.index()).flashing("error" -> "Error while addin ingrdient")) 
     } 
    } 
} 
Cuestiones relacionadas