2012-06-05 7 views
10

मेरे नियंत्रक कार्रवाई कोड इस तरह दिखता है:कैसे एक playframework 2 Async ब्लॉक में अपवाद को संभालने के लिए (स्केला)

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 (...) की ओर से एक वादा [घटक] लेकिन यह भी फेंक सकता एक कस्टम सत्यापन अपवाद। जब यह अपवाद फेंक दिया जाता है तो मैं टिप्पणी कोड वापस करना चाहता हूं।

वर्तमान में पेज 500 के रूप में प्रस्तुत करने और लॉग में मेरे पास है:

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

दो सवाल:

  • यह एक बुरा विचार मेरी सेवा से इस अपवाद वापस जाने के लिए है, वहाँ एक बेहतर है इस मामले को संभालने के लिए/अधिक स्केल तरीका?
  • मैं अपवाद कैसे प्राप्त करूं?
+0

कुछ दिन पहले एक बग तय किया गया था। [यह प्रतिबद्ध] देखें (https://github.com/playframework/Play20/commit/def888333ea435437edb7f70ca3b7f48877af1c7)। आप अपने 'ग्लोबल' ऑब्जेक्ट के 'ऑनरर' हुक में रनटाइम अपवादों को संभाल सकते हैं। –

+0

लेकिन स्थानीय रूप से अपवाद को पकड़ने का कोई तरीका नहीं है? – Somatik

+0

हां, आप इसे किसी अन्य अपवाद की तरह पकड़ सकते हैं, जैसा कि खेरूद के उत्तर में दिखाया गया है। –

उत्तर

2

मैं कहना चाहता हूँ एक शुद्ध कार्यात्मक तरीका एक प्रकार है कि वैध और त्रुटि राज्यों धारण कर सकते हैं उपयोग करने के लिए हो गया होता।

के लिए, आप Validation रूप scalaz

लेकिन अगर वह scalaz से अधिक से अधिक की जरूरत नहीं है (यदि आप ^^), तो आपको एक बहुत ही सरल सामान परिणाम और उसके रूप में एक Promise[Either[String, Ingredient]] का उपयोग कर इस्तेमाल कर सकते हैं का उपयोग कर सकते Async ब्लॉक में fold विधि। वह है, map जब मूल्य रिडीम किया जाता है और fold रिडीम किया जाता है तो मूल्य को रूपांतरित करने के लिए।

अच्छा बिंदु => कोई अपवाद नहीं => हर चीज के लिखे जाने की जांच :-)

संपादित

यह अधिक जानकारी का एक सा की आवश्यकता हो सकती है, यहाँ दो विकल्प हैं: पकड़ का प्रयास करें, @ खेराउड के लिए धन्यवाद) और या तो। 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

कोशिश-पकड़ समाधान प्ले 2.0.1 में काम नहीं कर रहा है, बग/पैच से जुड़ा हो सकता है जूलियन रेफरिंग कर रहा था? – 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")) 
     } 
    } 
}