【问题标题】:Scala Play - Action.async with BodyParser - Exception HandlingScala Play - 带有 BodyParser 的 Action.async - 异常处理
【发布时间】:2016-01-02 10:08:55
【问题描述】:

我将 Scala 与 Play 一起用于我的微服务。它有一个控制器,它使用带有自定义正文解析器的 Action.async 构造。这是一个示例代码:

def crud(param: String) = Action.async(SomeCustomBodyParser) { implicit request =>
    try {
      <some code>
    } catch {
      case _ => <Exception Handling logic>
    }
}

这段代码的问题是,如果SomeCustomBodyParser 中出现异常,它不会在catch 块中得到处理。我尝试了几种方法,将其提取到外部,然后手动处理,但未正确捕获异常。 Action.async 的代码表明它需要一段代码并在单独的上下文中执行它。我不清楚它是如何工作的。

如何处理异常并输出更好的异常消息。

【问题讨论】:

    标签: scala asynchronous playframework action body-parser


    【解决方案1】:

    Action.async 必须被赋予一个Future[Result],可以通过成功的Result 或失败来完成。

    任何failed Future 都会导致错误的HTTP 响应。

    Action.async { Future.failed(new Exception("Foo") }
    

    可以对此类错误的格式进行调整。

    【讨论】:

      【解决方案2】:

      这里的问题是您尝试使用 同步 错误处理程序来处理 异步 错误。 try catch 只能处理同步错误。 Future[_] 本质上是异步的,如果有的话,它会在你的 try catch 语句已经执行(并且可能在不同的线程中)之后抛出错误。

      相反,在 scala 中,我们通过使用来自 scalaz 的 OptionEither\/ 等数据结构来明确错误处理。所有这些 包装器 形成 Monad。

      在大多数异步服务器设置中,您想要的是 FutureEither 内部(或像 scalaz 的 \/ 这样的右偏变体。)这样,您可以抽象异步和错误处理。由于两个包装器都是 monad,您可以使用 Monad Transformers 将它们组合起来。这是一个很深的话题,如果您不熟悉它,需要大量学习,但这种数据结构的要点如下:

      class Task[E, A] {
        def flatMap[U](f: A => Task[E, U]): Task[E, U] = ??? // Use monad transformer here.
      }
      

      其中E 表示您的自定义错误的类型 - 您可能会通过代数数据类型(如sealed trait)来表示它们,其中case classA 是您的值类型。

      【讨论】:

      • 这里的问题是SomeCustomBodyParser抛出异常,try/catch根本没有运行。它是否会被异步执行是无关紧要的,因为它没有被执行。
      • 郑重声明,Either 不是 scala 中的单子。
      【解决方案3】:

      如果您的 BodyParser 抛出异常,或因其他原因失败,则 Action 内的任何代码都不会执行。 async 的性质在这里无关紧要。

      例如,下面代码中的System.exit 永远不会运行,因为 BodyParser总是 返回异常。

      package controllers
      
      import javax.inject.Inject
      
      import play.api.mvc._
      
      import scala.concurrent.Future
      
      class Application @Inject() extends Controller {
      
        def crud(param: String) = Action.async(
          parse.error[String](Future.failed(new RuntimeException("oh noes!")))
        ) { req =>
          System.exit(0)
          ???
        }
      
      }
      

      此代码生成此堆栈跟踪:

      play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[RuntimeException: oh noes!]]
          at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:254) ~[play_2.11-2.4.0.jar:2.4.0]
          at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:180) ~[play_2.11-2.4.0.jar:2.4.0]
      

      堆栈跟踪显示有一个 DefaultHttpErrorHandler 被调用,并从 BodyParser 抛出异常。

      ScalaErrorHandling 的文档提供了自定义或编写自己的示例。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-02-07
        • 2017-12-20
        • 2017-09-30
        • 1970-01-01
        • 2018-11-15
        • 2014-06-15
        • 2020-11-13
        相关资源
        最近更新 更多