Подтвердить что ты не робот

Почему будущее не восстанавливает исключения?

Я использую Scala, Play Framework 2.1.x и драйвер reiventemongo.

У меня есть вызов api:

def getStuff(userId: String) = Action(implicit request => {
    Async {
      UserDao().getStuffOf(userId = userId).toList() map {
        stuffLst => Ok(stuffLst)
      } 
    }
})

Он отлично работает в 99% случаев, но иногда он может терпеть неудачу (неважно, почему, это не проблема).

Я хотел восстановить в случае ошибки, поэтому добавил:

recover { case _ => BadRequest("")}

Но это не избавит меня от ошибок.
Я попробовал ту же концепцию на консоли scala, и она сработала:

import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
var f = future { throw new Exception("") } map {_ => 2} recover { case _ => 1}
Await.result(f, 1 nanos)

Это возвращает 1, как ожидалось.
Я в настоящее время завернул Async с помощью:

try{
  Async {...}
} catch {
  case _ => BadRequest("")
} 

И это уловляет ошибки.

Я просмотрел несколько документов scala Future в сети, и я озадачен, почему восстановление не работает для меня.

Кто-нибудь знает, почему? Что мне не хватает, чтобы разобраться?

4b9b3361

Ответ 1

Почему это не так, на самом деле имеет значение 100%. Если мы распространим код на несколько строк кода, вы поймете, почему:

def getStuff(userId: String) = Action(implicit request => {
  Async {
    val future = UserDao().getStuffOf(userId = userId).toList()
    val mappedFuture = future.map {
      stuffLst => Ok(stuffLst)
    }
    mappedFuture.recover { case _ => BadRequest("")}
  }
})

Итак, UserDao().getStuffOf(userId = userId).toList() возвращает вам будущее. Будущее представляет собой нечто, что, возможно, еще не произошло. Если эта вещь вызывает исключение, вы можете обработать это исключение в процессе восстановления. Однако в вашем случае ошибка происходит до того, как будущее будет даже создано, вызов UserDao().getStuffOf(userId = userId).toList() бросает исключение, а не возвращает будущее. Поэтому призыв к восстановлению будущего никогда не будет выполнен. Это эквивалентно выполнению этого в Scala repl:

import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
var f = { throw new Exception(""); future { "foo" } map {_ => 2} recover { case _ => 1} }
Await.result(f, 1 nanos) }

Очевидно, что это не сработает, поскольку вы никогда не создавали будущее, в первую очередь, вызывали исключение, прежде чем код создавал будущее.

Итак, решение заключается в том, чтобы либо обернуть ваш вызов UserDao().getStuffOf(userId = userId).toList() в блок catch try, либо выяснить, почему он не работает в любом методе, который вы вызываете, и улавливать исключение там, и возвращать неудавшееся будущее.

Ответ 2

Если у вас есть более поздняя версия Play, например 2.2.x, вы можете сделать это:

def urlTest() = Action.async {
    val holder: WSRequestHolder = WS.url("www.idontexist.io")
    holder.get.map {
      response =>
        println("Yay, I worked")
        Ok
    }.recover {
      case _ =>
        Log.error("Oops, not gonna happen")
        InternalServerError("Failure")
    }
}