2012-06-05 57 views
10

我的控制器操作的代碼如下所示:如何處理異常的playframework 2異步塊(斯卡拉)

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

我Service.addIngredient(...)返回一個無極[成分],但也可以拋出一個自定義的ValidationException。當拋出這個異常時,我想返回註釋的代碼。

目前的頁面呈現在500nm和在日誌中我有:

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

兩個問題:

  • 它是一個壞主意,我的服務回報這個例外,有沒有更好的/更多scala的方式來處理這種情況?
  • 如何捕捉異常?
+0

有已在幾天前修正了一個錯誤。看[這個提交](https://github.com/playframework/Play20/commit/def888333ea435437edb7f70ca3b7f48877af1c7)。您可以在'Global'對象的'onError'鉤子中處理運行時異常。 –

+0

但是沒有辦法在本地捕獲異常? – Somatik

+0

是的,你可以像其他異常一樣捕捉它,如kheraud的答案所示。 –

回答

2

我想說一個純粹的功能方式會是使用一個可以保存有效和錯誤狀態的類型。

對於這一點,你可以使用Validation形式scalaz

但是,如果不需要比從scalaz更多的(你會^^),你可以使用Promise[Either[String, Ingredient]]作爲結果,其使用非常簡單的東西在異步塊中使用方法fold。即map將兌換兌換承諾時兌換的價值和fold兌換的內容。

好點=>也不例外=>每一件事情鍵入檢查:-)

編輯

它可能需要一些更多的信息,這裏有兩種選擇:儘量捕,感謝@ kheraud)和任一。沒有把Validation,如果需要問我。 對象應用擴展控制器{

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

類型檢查解決方案顯然更好。感謝這些例子! – iwalktheline

+0

沒有問題。這裏的^^。乾杯 –

+0

Try-catch解決方案在2.0.1中不起作用,可能與Julien提到的bug /補丁有關?最有可能的是 – Somatik

0

難道你不能在異步塊中捕獲異常嗎?

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