2012-06-05 29 views
10

mã hành động điều khiển của tôi trông như thế này:Làm thế nào để xử lý các trường hợp ngoại lệ trong một play framework 2 Async khối (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()))))) 
     } 
     }) 
    } 

Service.addIngredient của tôi (...) trả về một Promise [Thành phần] nhưng cũng có thể ném một ValidationException tùy chỉnh. Khi ngoại lệ này được ném, tôi muốn trả lại mã nhận xét.

Hiện nay trang này hiển thị như 500 và trong các bản ghi tôi có:

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

Hai câu hỏi:

  • có phải là một ý tưởng tồi để trở lại ngoại lệ này từ dịch vụ của tôi, là có một tốt hơn/nhiều scala cách để xử lý trường hợp này?
  • Làm cách nào để nhận ngoại lệ?
+0

Có lỗi đã được khắc phục cách đây vài ngày. Xem [cam kết này] (https://github.com/playframework/Play20/commit/def888333ea435437edb7f70ca3b7f48877af1c7). Bạn có thể xử lý các ngoại lệ thời gian chạy trong móc 'onError' của đối tượng' Global' của bạn. –

+0

nhưng không có cách nào để bắt ngoại lệ tại địa phương? – Somatik

+0

Có, bạn có thể bắt nó giống như bất kỳ ngoại lệ nào khác, như được hiển thị trong câu trả lời của kheraud. –

Trả lời

2

Tôi muốn nói cách chức năng thuần túy là sử dụng loại có thể giữ trạng thái hợp lệ và lỗi.

Cho rằng, bạn có thể sử dụng hình thức Validation scalaz

Nhưng nếu không cần nhiều hơn thế từ scalaz (bạn sẽ ^^), bạn có thể sử dụng một công cụ rất đơn giản bằng cách sử dụng Promise[Either[String, Ingredient]] như là kết quả của nó và fold phương pháp trong khối Async. Tức là, map để chuyển đổi giá trị khi lời hứa được đổi và fold trên những gì được đổi. kiểm tra

Điểm tốt => không phải ngoại lệ => mọi thứ được gõ :-)

EDIT

Nó có thể cần một chút thông tin thêm, thì đây là hai lựa chọn: cố gắng nắm bắt, nhờ @kheraud) và cả hai. Không đặt số Validation, hãy hỏi tôi nếu cần. đối tượng Ứng dụng mở rộng Bộ điều khiển {

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

Giải pháp kiểm tra loại rõ ràng là tốt hơn. Cảm ơn các ví dụ! – iwalktheline

+0

Không có prob. Ở đây vì điều đó ^^. Chúc mừng –

+0

Giải pháp try-catch không hoạt động trong phiên bản 2.0.1, có thể liên quan đến lỗi/bản vá mà Julien đang đề cập đến? – Somatik

0

Bạn không thể chỉ bắt ngoại lệ trong khối Async của mình?

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")) 
     } 
    } 
} 
Các vấn đề liên quan