Rejection Handling in Akka Http.


While working on one my project which uses Akka Http for routing, I came across a situation where we had to use custom Rejection Handler. It was a worthy thing to be shared with all and hence this blog 😉
Rejections are used by Akka Http to help us handle error scenarios more aptly. When the filter directives do not let a request pass through their internal body because the filtering conditions are not met, a rejection occurs. This rejection flows through the chain of the routes to see if any route can handle it or not. If there is a route in the routing structure, that can handle it, its good else it generates a rejection.
Let me illustrate this with an example –

val route = 
  path("hello") {
    get {
      complete(HttpEntity(ContentTypes.`application/json`,"Hello"))
    }
  } ~ path("name") {
    get {
      complete(HttpEntity(ContentTypes.`application/json`,"Akka Http"))
    }
  }

If we access the route /hello, then we will successfully get response Hello.
But what if we hit the route /random?
Then what?
Well, that will give us a response saying The requested resource could not be found. This response was generated by handleNotFound method of RejectionHandler provided by Akka Http.

There are two more such methods in RejectionHandler:
1) handle
Handles certain rejections with the given partial function. The partial function produces the route which is executed when the defined rejection occurs.
2) handleAll
According to Akka Http Documentation handleAll “Handles all rejections of a certain type at the same time. This is useful for cases where your need access to more than the first rejection of a certain type, e.g. for producing the error message to an unsupported request method.”

Above you saw that the message route /random produced was The requested resource could not be found. But we may not want our program to display output just like this. We will want our output to be formatted. This is where we can customize rejections.

Consider this piece of code

implicit def rejectionHandler =
  RejectionHandler.newBuilder()
    .handleNotFound {
      val errorResponse = write(ErrorResponse(NotFound.intValue, "NotFound", "The requested resource could not be found.")) 
      complete(HttpResponse(NotFound, entity = HttpEntity(ContentTypes.`application/json`, errorResponse)))
    }
    .result()

where ErrorResponse is a case class like
case class ErrorResponse(code: Int, `type`: String, message: String)

We give an implicit definition using newBuilder() to build a new RejectionHandler and tell it how to handle the notFound cases.

Now if we hit /random, the output we will be

{
  "code": 404,
  "type": "NotFound",
  "message": "The requested resource could not be found."
}

which is more descriptive.

There are many Predefined Rejections that are provided by Akka Http which are very useful. I will illustrate two of them here.

1) MissingQueryParamRejection

This is generated when the request does not have adequate required query parameters.
Consider a route:

val route = path("check") {
  parameters('color, 'bgColor) { 
    (color, bgColor) => 
      val properResponse = write(ProperResponse(OK.intValue, s"Your preference is color $color with background color $bgColor.")) 
      complete(HttpResponse(OK, entity = HttpEntity(ContentTypes.`application/json`, properResponse)))
  }
}

Its rejection handler would look like

implicit def rejectionHandler =
  RejectionHandler.newBuilder()
    .handle {
      case MissingQueryParamRejection(param) =>
      val errorResponse = write(ErrorResponse(BadRequest.intValue, "Missing Parameter", s"The required $param was not found."))
      complete(HttpResponse(BadRequest, entity = HttpEntity(ContentTypes.`application/json`, errorResponse)))
    }
    .result()

So, if someone hits the route localhost:8080/check?color=red, then result would be

{
  "code": 400,
  "type": "Missing Parameter",
  "message": "The required bgColor was not found."
}

One thing to note is that if no parameter is provided, then it will only highlight the first parameter as missing.
So if the route `localhost:8080/check` is hit, then output would be

{
  "code": 400,
  "type": "Missing Parameter",
  "message": "The required color was not found."
}

2) AuthorizationFailedRejection

This rejection occurs when the user is not authorized to access a resource.

Consider the piece of code

val route = path("admin") {
  parameters('username, 'password) {
    (username, password) =>
      if (username.equals("knoldus") && password.equals("knoldus")) {
        val properResponse = write(ProperResponse(OK.intValue, "Welcome!!!"))
        complete(HttpResponse(OK, entity = HttpEntity(ContentTypes.`application/json`, properResponse)))
      } else {
        reject(AuthorizationFailedRejection)
      }
  }
}

implicit def rejectionHandler =
  RejectionHandler.newBuilder()
    .handle { case AuthorizationFailedRejection =>
      val errorResponse = write(ErrorResponse(BadRequest.intValue, "Authorization", "The authorization check failed for you. Access Denied."))
      complete(HttpResponse(BadRequest, entity = HttpEntity(ContentTypes.`application/json`, errorResponse)))
    }

Here you will notice that it is entirely possible to explicitly reject a route and then handle it the way you want to by using Rejection Handler. This comes handy when you want to explicitly reject a route and process it’s handling. So now, if you hit the route with wrong values in parameters localhost:8080/admin?username=knol&password=dus, we will get a response

{
  "code": 400,
  "type": "Authorization",
  "message": "The authorization check failed for you. Access Denied."
}

Again, looking descent.

Therefore we have through this blog post seen how Rejection Handler can be aptly used. There are many predefined handlers which you can go through in Akka Http documentation. You may find my entire code here.
In the next blog post, I will talk about testing rejection handlers.
Till then, happy coding 🙂


KNOLDUS-advt-sticker

Advertisements
This entry was posted in Akka, akka-http, Scala. Bookmark the permalink.

2 Responses to Rejection Handling in Akka Http.

  1. Pingback: Testing Rejection Handling in Akka-Http | Knoldus

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s