无法正确处理错误

时间:2018-10-22 06:51:06

标签: scala playframework-2.6

我已经找到了我的应用程序无法正常运行的原因,但我不知道如何解决该问题。总而言之,我的应用程序具有一个自定义错误处理程序,如果发生错误,则将调用该处理程序。错误处理程序发送json消息。但是在一个应用程序启动错误情况下(Future失败),我想将用户Redirect转到首页,而不是发送json消息。但这不会发生,因为自定义错误处理程序先发送json消息,然后才能从Redirect的{​​{1}}发送Future

应用程序的一项功能是注册验证。用户单击具有令牌的recover。单击url时,将调用url verifyUser。它进行一些检查(使用使用Action的数据库查询),并根据成功或失败而发送带有FutureRedirect属性的signup=success(此处未确定失败)基于数据库中是否存在某些内容)。但是,如果signup=error失败(我查询了一个错误的字段,该字段不是数据库架构的一部分),我想再次Future,但是由于自定义错误处理程序在{{ 1}}。如何使我的应用程序重定向?

Redirect

自定义错误处理程序

recover

我能够验证isse,因为我看到了两次调试(一个来自自定义错误处理程序,另一个来自恢复)

val result:Future[Result] = for{tokenOption:Option[UserToken] <- userTokenRepo.findOne(UserTokenKey(UUID.fromString(token))) //generator 1 - get token from database userOption:Option[User] <- if (tokenOption.isDefined) userRepo.findOne(tokenOption.get.userKeys) else Future.successful(None) //generator2. found token, look for corresponding user to which the token belongs modifiedUser:Option[User] <- if (userOption.isDefined) confirmSignupforUser(userOption.get) else Future.successful(None) //generator 3. found user and token. Update profile deletedToken:Option[UserTokenKey] <- if(modifiedUser.isDefined) userTokenRepo.delete(UserTokenKey(UUID.fromString(token))) else Future.successful(None) } yield { //check if we have user and token and modified user here. If any is missing, return error else success println("db query results tokenOption: "+tokenOption+", userOption: "+userOption+" : modifiedUserOption: "+modifiedUser+", deletedToken: "+deletedToken) if(tokenOption.isDefined && userOption.isDefined && modifiedUser.isDefined && deletedToken.isDefined) Redirect("http://localhost:9000/home"+";signup=success")//TODOM - pick from config else /*TODOM - when redirecting with error, can provide additional info why sign up failed*/ if(tokenOption.isEmpty) Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config else if(userOption.isEmpty) Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config else if(modifiedUser.isEmpty) Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config else //this shouldn't happen. Unexpected Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config } result.recover { case x => { println("Future failed in validateUserSession. Recovering. Returning Internal Server Error" + x) //before this Redirect, the custom error handler sends json response Redirect("http://localhost:9000/home"+";signup=error")//TODOM - pick from config } }

class CustomHttpErrorHandler extends HttpErrorHandler { def onClientError(request: RequestHeader, statusCode: Int, message: String) = { println("client error: request "+request+", statusCode: "+statusCode+", message:"+message) Future.successful( Status(statusCode)(Json.toJson(JsonResultError(message))) ) } def onServerError(request: RequestHeader, exception: Throwable) = { println("server error: request: "+request+", exception: "+exception.getMessage) Future.successful( InternalServerError(Json.toJson(JsonResultError(exception.getMessage))) ) } }

我可以尝试根据自定义错误处理程序中接收到的异常进行检查,但是我认为它太通用了,可能不是一个好的设计方法。

1 个答案:

答案 0 :(得分:0)

错误可能在创建Future之前发生,因此代码将引发由默认处理程序处理而不是由Future捕获的异常。

具体来说,表达式

userTokenRepo.findOne(UserTokenKey(UUID.fromString(token)))

是在当前线程中求值的,因此此代码中的任何异常都不会被捕获,并将调用默认的错误处理程序。

解决方案是在Try中进行计算,如果有错误,则立即处理错误。

这可能是这样的:

for {
  tokenKey <- Future.fromTry(Try(UserTokenKey(UUID.fromString(token))))
  tokenOption <- userTokenRepo.findOne(tokenKey)
  userOption <- tokenOption.fold(Future.successful)(userRepo.findOne(_.userKeys)) //generator2. found token, look for corresponding user to which the token belongs
  modifiedUser <- userOption.fold(Future.successful)(confirmSignupforUser) //generator 3. found user and token. Update profile
  ...

此代码中的任何异常都将导致Future代码处理失败的recover