srvy srvy - 1 month ago 10
Scala Question

Handle exception in Play2.5/scala controller

I would like to catch exception and redirect to a custom page in the following code. However, exceptions are not caught/seen.

def addItemWithParts(item: Item, parts: Seq[String]): Future[Int] = {
... // May throw exceptions
}

def handleAddItem = auth.SecuredAction.async { implicit request =>
itemForm.bindFromRequest.fold(
formWithErrors => {
Future.successful(
Redirect(controllers.www.routes.ItemController.startAddItem()).flashing(
"error" -> "Bad item add input"
)
)
},
item => {
for {
ue <- usersService.findUserEntryByEmail(request.identity.email)
} yield ue match {
case Some(ue) =>
val itemObj = Item(item.name, item.description, ue.companyId)
val xx = itemsService.addItemWithParts(itemObj, Seq(item.parts)) <-- want to catch exception thrown by this function
/*
* COMMENTED CODE PRINTS EXCEPTION ON CONSOLE, BUT DONT KNOW HOW TO REDIRECT/OK...
xx onComplete {
case Success(x) => {
println("Added ITEM: " + x)
Redirect(controllers.www.routes.Dashboard.dashboard)
}
case Failure(exp) => {
println("Exception while adding ITEM: " + exp)
Ok("Got exception: " + exp)
}
}
*/
Redirect(controllers.www.routes.Dashboard.dashboard) // +++
case None =>
Ok("Bad")
}
}
)
}


I thought I can do Redirect() from onComplete success instead of at line marked "+++" but I get this compilation error:

type mismatch;
[error] found : Unit
[error] required: play.api.mvc.Result
[error] case Some(ue) =>
[error] ^
[error] one error found


I checked play documentation, it talks about adding onServerError to ErrorHandler (centralized) but I want to know what I am missing in doing it this way.

I am still learning Scala, any help is deeply appreciated.

Answer

onComplete returns Unit. You can do only side effecting operations using onComplete.

Use map, flatMap for composing and building new computations. Use recover and recoverWith to handle exceptions and return something on exceptions.

here is how you can do this

val result = 
for {
 ueOpt <- usersService.findUserEntryByEmail(request.identity.email)
 result <- ueOpt match {
            case Some(ue) =>

               val itemObj = Item(item.name, item.description, ue.companyId)
               val foo = itemsService.addItemWithParts(itemObj, Seq(item.parts))

              foo.map { value =>
                Redirect(controllers.www.routes.Dashboard.dashboard)
              }.recover { case th =>
                InternalServerError("bad things happen in life.")
              }

            case None => Future.successful(BadRequest("no item found"))
          }
} yield result

Future provides methods like map, flatMap to build new computations with result of the current future. Also future provides recover and recoverWith to build computations when current future throws exceptions.

def bar: Future[Int] = ???

bar.map { intValue =>
  //doSomething
}.recover {
  case ex: SQLException => //return some value
  case _ => //ignore other exceptions and return default value
}
Comments