Krešimir Nesek
Krešimir Nesek

Reputation: 5492

How to handle an exception that happens in a future in scala

I'm writing a controller method in play framework that calls a function that returns a Future that may also throw an Exception. I'm having trouble figuring out how to catch and handle that Exception.

Here's what I tried:

  def openIDCallback = Action { implicit request =>
    Async (
      Try(OpenID.verifiedId) match {
        case Failure(thrown) => {
          PurePromise(Ok("failed: " + thrown))
        }
        case Success(successResult) => {
          successResult.map( userInfo => {
            Ok(userInfo.id + "\n" + userInfo.attributes)
          })
        }
      }
    )
  }

OpenID.verifiedId is the function from Play's OpenId api that returns Future[UserInfo]. Here's the source of that function:

def verifiedId(queryString: Map[String, Seq[String]]): Future[UserInfo] = {
    (queryString.get("openid.mode").flatMap(_.headOption),
      queryString.get("openid.claimed_id").flatMap(_.headOption)) match { // The Claimed Identifier. "openid.claimed_id" and "openid.identity" SHALL be either both present or both absent.
      case (Some("id_res"), Some(id)) => {
        // MUST perform discovery on the claimedId to resolve the op_endpoint.
        val server: Future[OpenIDServer] = discovery.discoverServer(id)
        server.flatMap(directVerification(queryString))(internalContext)
      }
      case (Some("cancel"), _) => PurePromise(throw Errors.AUTH_CANCEL)
      case _ => PurePromise(throw Errors.BAD_RESPONSE)
    }
  }

As shown above, function can return PurePromise(throw Errors.AUTH_CANCEL) and PurePromise(throw Errors.BAD_RESPONSE). My attempt at solution handles successes correctly, but on exceptions I get:

play.api.Application$$anon$1: Execution exception[[AUTH_CANCEL$: null]]

My question is how do I catch and handle these exceptions in my controller method?

Upvotes: 5

Views: 1811

Answers (1)

senia
senia

Reputation: 38045

You should use recover method of Future instead of Try like this:

Async (
  OpenID.verifiedId.
    map{userInfo => Ok(userInfo.id + "\n" + userInfo.attributes)}.
    recover{ case thrown => Ok("failed: " + thrown) }
)

Try can help you in case verifiedId throws an exception instead of returning Future. In your case verifiedId successfully returns Future (even if there will be an exception in this Future).

Upvotes: 10

Related Questions