2012-06-05 10 views
10

Il mio codice azione di controllo è simile al seguente:Come gestire le eccezioni in un blocco play framework 2 Async (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()))))) 
     } 
     }) 
    } 

mio Service.addIngredient (...) restituisce una promessa [ingrediente], ma può anche buttare una ValidationException personalizzata. Quando viene generata questa eccezione, desidero restituire il codice commentato.

Attualmente pagina rende come 500 e nei registri ho:

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

Due domande:

  • E 'una cattiva idea di tornare questa eccezione dal mio servizio, c'è una migliore/più scala per gestire questo caso?
  • Come rilevare l'eccezione?
+0

C'era un bug che è stato fissato un paio di giorni fa. Vedi [questo commit] (https://github.com/playframework/Play20/commit/def888333ea435437edb7f70ca3b7f48877af1c7). Puoi gestire le eccezioni del runtime nell'hook 'onError' del tuo oggetto' Global'. –

+0

ma non c'è modo di rilevare l'eccezione localmente? – Somatik

+0

Sì, puoi prenderlo come qualsiasi altra eccezione, come mostrato nella risposta del kheraud. –

risposta

2

Direi che un modo puramente funzionale sarebbe stato utilizzare un tipo che può contenere stati validi e di errore.

Per questo, è possibile utilizzare Validation forma scalaz

Ma se non hanno bisogno di più di questo da scalaz (si ^^), è possibile utilizzare una roba molto semplice utilizzando un Promise[Either[String, Ingredient]] come il risultato e la sua Metodo fold nel blocco Async. Cioè, map per convertire il valore quando la promessa è riscattata e fold su ciò che viene riscattato. controllo

Il buon punto => non fa eccezione => ogni cosa viene digitato :-)

EDIT

Si potrebbe essere necessario un po 'di informazioni di più, qui ci sono le due opzioni: provare cattura, grazie a @kheraud) ed entrambi. Non ho inserito lo Validation, chiedimi se necessario. oggetto Application estende 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 soluzione di tipo controllato è chiaramente migliore. Grazie per gli esempi! – iwalktheline

+0

Nessun prob. Ecco per quello ^^. Cheers –

+0

La soluzione try-catch non funziona nel gioco 2.0.1, potrebbe essere correlata al bug/patch a cui si stava riferendo Julien? – Somatik

0

Non riesci a rilevare l'eccezione nel blocco 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")) 
     } 
    } 
} 
Problemi correlati