Reputation: 415
currently I'm implementing a REST API with Scala and the Play! Framework. When a user submits data da is not correct, or the user is not allowed to view a resource the API has to response with BadRequest or Forbidden.
I don't want to have a huge bulk of nested if-else-statements to check every permission. So I just want to raise RuntimeExceptions like BadRequestException or ForbiddenException.
class MapableRestException(msg: String) extends RuntimeException(msg)
class NotFoundException(msg: String) extends MapableRestException(msg)
class ForbiddenException(msg: String) extends MapableRestException(msg)
class BadRequestException(msg: String) extends MapableRestException(msg)
In the Global Class I override onError
override def onError(request: RequestHeader, ex: Throwable): Future[Result] = ex.getCause match {
case notfound: NotFoundException => {
Future.successful(NotFound(JsonHelper.toJsonAll(ErrorDTO(notfound.getMessage()))).as(JSON))
}
case forbidden: ForbiddenException => {
Future.successful(Forbidden(JsonHelper.toJsonAll(ErrorDTO(forbidden.getMessage()))).as(JSON))
}
case badrequest: BadRequestException => {
Future.successful(BadRequest(JsonHelper.toJsonAll(ErrorDTO(badrequest.getMessage))).as(JSON))
}
case _ => super.onError(request, ex)
}
But when an one of the above Exceptions is thrown the stacktrace is still printed on the console?
Greetings, Daniel
Upvotes: 3
Views: 1893
Reputation: 8901
I too would be interested in a more direct answer to this, but for the meantime, here's a workaround.
Create an ActionBuilder
that runs your block and uses recover
(or recoverWith
, if you need to do async stuff) to do your global error handling, then use it in place of Action
:
object ActionWithErrorHandling extends ActionBuilder[Request] {
override def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]): Future[Result] = {
block(request) recover {
case e: PermissionDenied => Forbidden(e.getMessage)
case e: ItemNotFound => NotFound(e.getMessage)
}
}
}
And in controllers:
def myAction = ActionWithErrorHandling { implicit request =>
// Non-exception code path...
}
This is arguably a more sanitary approach since it preserves the Global onError for genuine oh-dear situations.
Upvotes: 4