【问题标题】:Play framework async action: Future recover not working播放框架异步操作:未来恢复不起作用
【发布时间】:2016-11-28 03:05:13
【问题描述】:

我的 play 2 应用中有以下代码:

控制器:

...
    def getUserById(id: Long) = Action.async {
        try {
          userService.findById(id)
            .map(u => Ok(Json.toJson(u))
            .recover {
              case e: Exception => Ok("Exception got")
            }
        }
      }
...

服务:

...
  override def findAll: Future[Seq[User]] = {
    throw new Exception("Error 1")
  }
...

但在控制器中,我无法捕获服务中引发的异常(恢复块以某种方式被忽略)。而是显示带有异常“错误 1”的播放标准错误页面。

我做错了什么?

【问题讨论】:

  • 你应该Future.failed(new Exception("Error 1"))

标签: scala asynchronous playframework future


【解决方案1】:

您的代码在返回 Future 之前引发异常,因此您应该:

override def findAll: Future[Seq[User]] = Future {
  throw new Exception("Error 1")
}

或者只是:

override def findAll: Future[Seq[User]] = 
  Future.failed(new Exception("Error 1"))

这样 - 异常将被包装在Future 的实例中,因此每个订阅者都可以异步获取它并由recover 处理。否则,您必须通过使用 try{ findAll(...) } catch {...} 包装来同步处理失败。

附:抛出异常is not referentially transparent,这就是为什么有时很难理解这种行为。将错误包装到 Future 中的方法更纯粹,所以我希望这样可以使代码更清晰。

【讨论】:

  • 哇,谢谢,乍一看并不明显,现在我清楚了。
  • 顺便说一句,异步操作中的 try catch 应该如何?在你回答之后,我有 ... try{} catch { case e: Exception => {val f = Future {} f.map(_ => Ok("Exception !!!")) }... 有没有在 catch 块中返回 future 的另一种方法?
  • @moreo 我的回答实际上是根本不建议您使用try {} catch。您所需要的只是与以前相同的恢复,只是不要从您的方法中抛出异常 - 使用 def findAll = Future.failed(new Exception("Error 1")) 代替,然后使用类似:findAll.map(...).recover(e => Ok(ErrorCode, e.getMessage))
  • @moreo 如果您应该捕获一些未知异常(例如某些外部服务正在抛出它 - 我相信最好在 def findAll 内部进行,例如 flatMap 然后:def findAll = try{serviceThatMightThrowUncheckedException(); some other code...} catch {case NonFatal(e) => Future.failed(e)}(你也可以使用scala.util.Try)
  • 该死的,刚看到这个答案。我的应用在期货中充满了throw new Exception(msg),而不是Future.failure(new Exception(msg))
猜你喜欢
  • 2014-12-14
  • 1970-01-01
  • 1970-01-01
  • 2014-03-27
  • 2018-07-27
  • 2011-11-09
  • 2017-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多