我的控制器操作代码如下所示:
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(...)返回Promise [Ingredient],但也可以抛出自定义的ValidationException。抛出此异常时,我想返回注释代码。
目前页面呈现为500并且在我拥有的日志中:
play - 等待承诺,但收到错误:有名字的成分 'test'已经存在。 services.ValidationException:具有名称的成分 'test'已经存在。
两个问题:
答案 0 :(得分:2)
我会说纯粹的功能方式是使用一种可以保存有效和错误状态的类型。
为此,您可以使用Validation表单scalaz
但是如果不需要scalaz(你会^^),你可以使用一个非常简单的东西,使用Promise[Either[String, Ingredient]]
作为结果,并在异步块中使用fold
方法。也就是说,map
转换兑换承诺时的值,并fold
转换兑换的内容。
好点=>没有例外=>每件事都是打字检查: - )
修改强>
它可能需要更多信息,这里有两个选项:try catch,感谢@kheraud)和Either。没有放Validation
,如果需要请问我。
object应用程序扩展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 :(得分: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"))
}
}
}