如何使用 ActionBuilder 和 Action 组合处理错误?

How can I handle errors using ActionBuilder, and Action composition?

我有两个操作:

object ErrorHandler extends
    ActionBuilder[Request]{
  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    try {
      block(request)
    } catch {
      case ex: Throwable => Future.successful{
        InternalServerError(
          Json.obj(
            "status" -> JsString("error")
          )
        )
      }
    }
  }
}

还有一个动作:

object WithFieldAction extends
    ActionBuilder[WithFieldRequest] with ActionTransformer[Request, WithFieldRequest] {

  val FIELD = "field"

  def transform[A](request: Request[A]) = Future.successful {
    new WithFieldRequest(request.getQueryString(FIELD), request)
  }
}

我结合了这个动作:

def index = (ErrorHandler andThen WithFieldAction) { implicit request =>
  1 / 0
}

但是 ErrorHandler 没有捕捉到异常,我看到了默认的错误页面。我该如何解决? (我不能使用全局错误处理程序,因为我想为不同的端点组使用不同的处理程序)

问题是异常在 Future 内抛出并保留。这意味着当你调用 block(request) 时,它只是去 return 一个 Future[Result] 来包装最终的异常(一个失败的 Future)。 Future 不应该让异常逃脱它,所以包装在 try/catch 中不会做任何事情。

相反,您需要 recover Future

def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
  block(request) recover { ex: Throwable =>
    InternalServerError(
      Json.obj(
        "status" -> JsString("error")
      )
    )
  }
}

recover 接受 PartialFunction[Throwable, ?],因此您可以根据需要对异常进行细粒度处理。例如,您可以只恢复 java.lang.ArithmeticException,如果您愿意,可以让所有其他故障传播到 Play 的错误处理。