4
votes

I have following code in my play 2 app:

Controller:

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

Service:

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

But in controller I cannot catch exception thrown in the service (recover block somehow ignored). Instead, play standard error page with Exception "Error 1" is displayed.

What am I doing wrong?

1
you should Future.failed(new Exception("Error 1"))dk14

1 Answers

6
votes

Your code throws an exception before returning a Future, so you should either:

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

or just:

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

In that way - an exception will be wrapped inside Future's instance, so every subscriber can get it asynchronously and process by recover. Otherwise you'd had to process a failure synchronously by wrapping it with try{ findAll(...) } catch {...}.

P.S. throwing an exception is not referentially transparent, so that's why sometimes it's hard to understand the behaviour. The approach with error being wrapped into a Future is more pure, so I'd prefer that to make code more clear.