恢复未来时播放控制器操作中的类型不匹配
Type mismatch in Play controller action when recovering a future
我在 return scala play controller 方法中的正确类型时遇到问题 有人可以在这里给我提示吗?我正在使用 for comprehantion 来处理 returns 一个 Future 的两个服务方法,我想优雅地处理结果和错误。
执行此操作的最佳做法是什么?
def registerUser = Action { implicit request =>
Logger.info("Start play actoin")
RegisterForm.form.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.register(formWithErrors))
},
formData => {
val registerResult = for {
reCaptchaOk <- registerUserService.checkRecaptcha(formData.gRecaptchaResponse)
userId <- registerUserService.registerUser(formData) if reCaptchaOk
} yield userId
registerResult.map(
result => Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString))
.recover{
e => handleRegisterError(e)
}
})
}
def handleRegisterError(cause: Throwable)(implicit request: Request[_]) : Result = {
val form = RegisterForm.form.bindFromRequest
cause match {
case dae: DataAccessException =>
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists") ,ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"),ERROR)))
}
BadRequest(views.html.register(form,globalError))
case _ =>
BadRequest(views.html.register(form))
}
错误:
[error] (compile:compileIncremental) Compilation failed
[info] Compiling 1 Scala source to C:\repos\scala\SocerGladiatorWeb\target\scala-2.11\classes...
[error] C:\repos\scala\SocerGladiatorWeb\app\controllers\RegisterController.scala:56: type mismatch;
[error] found : Throwable => play.api.mvc.Result
[error] required: PartialFunction[Throwable,?]
[error] e => handleRegisterError(e)
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
简答
您需要部分功能来恢复未来的故障:
def handleRegisterError(implicit request: Request[_]): PartialFunction[Throwable, Result] = {
case dae: DataAccessException =>
val form = RegisterForm.form.bindFromRequest
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists"), ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"), ERROR)))
}
BadRequest(views.html.register(form, globalError))
case _ =>
val form = RegisterForm.form.bindFromRequest
BadRequest(views.html.register(form))
}
然后将控制器代码更改为
registerResult
.map { result =>
Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString)
}
.recover {
handleRegisterError
}
另请注意,您需要一个异步操作,即
def registerUser = Action.async { implicit request =>
...
}
因为您返回的不是 Result
,而是 Future[Result]
。您可以在 Play docs.
中找到有关操作的更多信息
详情
如果您查看 Future
的 recover
方法的文档(参见 here),您会发现它需要 pf: PartialFunction[Throwable, U]
.
部分函数就像普通函数一样,但它们可能会拒绝某些值(例如,这里的 recover 方法不接受所有异常,而只接受主体中指定的异常)。
定义偏函数需要特殊的语法。它非常像模式匹配,但没有匹配表达式。
Future(someAsyncWork).recover {
case my: MyException => ....
case _ => ....
}
这里我们使用内联的部分恢复函数,所以类型会自动推断,但如果你想将恢复定义为一个单独的函数,你需要明确说明它的类型。
高级
部分函数语法(不带 match
关键字的模式匹配)在大多数情况下非常简洁和方便,但有时您需要的不止于此。
例如,请注意,使用此语法,我们必须在恢复函数中复制部分代码 (val form = RegisterForm.form.bindFromRequest
)。
虽然在您的情况下可能有更好的解决方案,但您始终可以将普通函数转换为部分函数。首先你需要定义一个 Throwable => Option[Result]
类型的函数,然后你可以使用 Function#unlift
将其转换为所需的偏函数。
您也可以直接继承 PartialFunction
and implement its two methods (apply and isDefinedAt).
我在 return scala play controller 方法中的正确类型时遇到问题 有人可以在这里给我提示吗?我正在使用 for comprehantion 来处理 returns 一个 Future 的两个服务方法,我想优雅地处理结果和错误。
执行此操作的最佳做法是什么?
def registerUser = Action { implicit request =>
Logger.info("Start play actoin")
RegisterForm.form.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.register(formWithErrors))
},
formData => {
val registerResult = for {
reCaptchaOk <- registerUserService.checkRecaptcha(formData.gRecaptchaResponse)
userId <- registerUserService.registerUser(formData) if reCaptchaOk
} yield userId
registerResult.map(
result => Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString))
.recover{
e => handleRegisterError(e)
}
})
}
def handleRegisterError(cause: Throwable)(implicit request: Request[_]) : Result = {
val form = RegisterForm.form.bindFromRequest
cause match {
case dae: DataAccessException =>
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists") ,ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"),ERROR)))
}
BadRequest(views.html.register(form,globalError))
case _ =>
BadRequest(views.html.register(form))
}
错误:
[error] (compile:compileIncremental) Compilation failed
[info] Compiling 1 Scala source to C:\repos\scala\SocerGladiatorWeb\target\scala-2.11\classes...
[error] C:\repos\scala\SocerGladiatorWeb\app\controllers\RegisterController.scala:56: type mismatch;
[error] found : Throwable => play.api.mvc.Result
[error] required: PartialFunction[Throwable,?]
[error] e => handleRegisterError(e)
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
简答
您需要部分功能来恢复未来的故障:
def handleRegisterError(implicit request: Request[_]): PartialFunction[Throwable, Result] = {
case dae: DataAccessException =>
val form = RegisterForm.form.bindFromRequest
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists"), ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"), ERROR)))
}
BadRequest(views.html.register(form, globalError))
case _ =>
val form = RegisterForm.form.bindFromRequest
BadRequest(views.html.register(form))
}
然后将控制器代码更改为
registerResult
.map { result =>
Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString)
}
.recover {
handleRegisterError
}
另请注意,您需要一个异步操作,即
def registerUser = Action.async { implicit request =>
...
}
因为您返回的不是 Result
,而是 Future[Result]
。您可以在 Play docs.
详情
如果您查看 Future
的 recover
方法的文档(参见 here),您会发现它需要 pf: PartialFunction[Throwable, U]
.
部分函数就像普通函数一样,但它们可能会拒绝某些值(例如,这里的 recover 方法不接受所有异常,而只接受主体中指定的异常)。 定义偏函数需要特殊的语法。它非常像模式匹配,但没有匹配表达式。
Future(someAsyncWork).recover {
case my: MyException => ....
case _ => ....
}
这里我们使用内联的部分恢复函数,所以类型会自动推断,但如果你想将恢复定义为一个单独的函数,你需要明确说明它的类型。
高级
部分函数语法(不带 match
关键字的模式匹配)在大多数情况下非常简洁和方便,但有时您需要的不止于此。
例如,请注意,使用此语法,我们必须在恢复函数中复制部分代码 (val form = RegisterForm.form.bindFromRequest
)。
虽然在您的情况下可能有更好的解决方案,但您始终可以将普通函数转换为部分函数。首先你需要定义一个 Throwable => Option[Result]
类型的函数,然后你可以使用 Function#unlift
将其转换为所需的偏函数。
您也可以直接继承 PartialFunction
and implement its two methods (apply and isDefinedAt).