{{announcement.body}}
{{announcement.title}}

Rejection in Akka HTTP: Handle Errors Properly

DZone 's Guide to

Rejection in Akka HTTP: Handle Errors Properly

Learn more about rejection in Akka HTTP and how it helps in handling error scenarios in your application.

· Java Zone ·
Free Resource

Learn more about rejection in Akka HTTP and how it helps in handling error scenarios in your application.

In this blog, we will demonstrate rejection in the Akka HTTP and how it helps in handling error scenarios in our application. Let's get started.

The concept of rejection in Akka HTTP helps to properly deal with error scenarios. For example, with a filtering directive, like the get directive, it does not allow the request to pass through its inner route. As the filter condition is not satisfied, a rejection concept is invoked.

The ~ (Tilde) operator is also called the chaining operator. As it is used to connect two or more paths, the Request allows flowing through the routing structure. And try to find another route that can complete it. If a request does not find any route, it will generate a rejection.

You may also like: Rejection Handling in Akka HTTP

By default, a response for rejected requests was generated by the “handle not found” method of  RejectionHandler.

Scala




xxxxxxxxxx
1


 
1
trait RejectionHandler extends (immutable.Seq[Rejection] ⇒Option[Route])



 We will understand this in detail through the following example:

Scala




xxxxxxxxxx
1


 
1
val route: Route =
2
 path("hello") {
3
   get {
4
     complete(HttpResponse(entity = "Hello world"))
5
   }    } ~
6
   path("ping") {
7
     get {
8
       complete(HttpResponse(entity = "PONG!"))  }
9
 }



If we access the path with “hello,” then we will get the response “Hello world.” At the same time, we will get a response “PONG!” for the route “ping.”

You may have questions like: "What happens if we hit any other route?" We’ll get the following response: “The requested resource could not be found.”

If we want this response in a more descriptive way, we can customize the RejectionHandler. The easiest way to construct a RejectionHandler is with RejectionHandler.newBuilder() that Akka HTTP provides.

Refer to following code snippet:

Scala




xxxxxxxxxx
1
10


 
1
implicit def rejectionHandler = RejectionHandler.newBuilder()
2
 .handleNotFound {
3
 complete(HttpResponse(NotFound,   entity = HttpEntity(ContentTypes.`application/json`, Json.stringify(
4
     Json.toJson(
5
       ErrorResponse(
6
         NotFound.intValue, "NotFound", "The requested resource could not be found.")
7
     )
8
   )
9
 )))
10
}



We’ll get the response as follows:

Java




xxxxxxxxxx
1


 
1
{
2
   "code": 404,
3
   "message": "NotFound",
4
   "reason": "The requested resource could not be found."
5
}



Where  ErrorResponse is the case class:

 case class ErrorResponse(code: Int, message: String, reason: String) 

There are three helper methods in RejectionHandler:

1. handleNotFound(Route):

As described in the above example, “Resource Not Found” is special. As it is represented with an empty rejection set, the handle not found helper lets you specify the “recovery route” for this case.

2. handle(PartialFunction[Rejection, Route]):

Handles provided the type of rejection with given partial function. This partial function produces a route, which is run when certain rejection occurs.

3. handleAll[T <: Rejection: ClassTag](f: immutable.Seq[T] => Route):

Handles all rejections of a certain type at the same time. This is useful for cases where you need access to more than the first rejection of a certain type, e. g. for producing the error message to an unsupported request method.

There are many predefined rejections that are provided by Akka HTTP. They are MethodRejection,  AuthorizationFailedRejection, MissingCookieRejection, and  MissingQueryParamRejection.

We invoked these handle calls through implicit definition using “newBuilder()” to build a new  RejectionHandler. We can tell this handler how to handle a particular rejection.

1. MethodRejection:

This rejection occurs when a user uses an unsupported method to access request. In the following example, a supported method is get. This will generate a rejection when we try to access using post,  put, or any other method.

Scala




xxxxxxxxxx
1
20


 
1
val route: Route =
2
 path("hello") {
3
   get {
4
     complete(HttpResponse(OK,   entity = HttpEntity(ContentTypes.`application/json`, Json.stringify(
5
       Json.toJson(
6
         ProperResponse(
7
           OK.intValue,"Hello world")))
8
     )))}
9
 }
10
implicit def rejectionHandler = RejectionHandler.newBuilder()
11
 
          
12
 .handleAll[MethodRejection] { methodRejections =>
13
 val names = methodRejections.map(_.supported.name)
14
 complete(HttpResponse(MethodNotAllowed,   entity = HttpEntity(ContentTypes.`application/json`, Json.stringify(
15
   Json.toJson(
16
     ErrorResponse(
17
       MethodNotAllowed.intValue, "Method Rejection", s"Method not supported! Supported for : ${names mkString "or"}!"))
18
 )
19
 )))
20
}
14
 complete(HttpResponse(MethodNotAllowed,   entity = HttpEntity(ContentTypes.`application/json`, Json.stringify(



For unsupported method, the output will be: 

Java




xxxxxxxxxx
1


1
{
2
   "code": 405,
3
   "message": "Method Rejection",
4
   "reason": "Method not supported! Supported for : GET!"
5
}



2. MissingQueryParamRejection:

MissingQueryParamRejection arises when the user doesn't mention the required parameters in  Request.

In the given example, if we hit URL, localhost:8080/paint?color = red.

In this scenario, the Response would be:

Request has missing required query parameter for = ‘bgColor’.

And if we hit URL, we get “localhost:8080/paint?bgColor = red." Then, Response will be: Request has missing required query parameter for = ‘color’.

When we mention both parameters in the URL localhost:8080/paint?bgColor=red&color=blue, then we get the proper result:

Java




xxxxxxxxxx
1


1
{
2
   "code": 200,
3
   "message": "You mention color is blue and background color is red"
4
}



3. AuthorizationFailedRejection:

This Rejection is created by the “authorize” directive. This signals that the request was rejected because the user is not authorized. 

Let’s see an example:

Scala




xxxxxxxxxx
1
26


 
1
val route: Route =
2
path("login") {
3
   parameters('username, 'password) {
4
     (username, password) =>
5
       if (username.equals("knoldus") && password.equals("pune")) {
6
         complete(HttpResponse(NotFound,entity = HttpEntity(ContentTypes.`application/json`, Json.stringify(
7
           Json.toJson(
8
             ProperResponse(
9
               OK.intValue, "Login Successful")))
10
         )))}
11
        else {
12
         reject(AuthorizationFailedRejection)
13
       }  } }
14
 
          
15
implicit def rejectionHandler =
16
 RejectionHandler.newBuilder()
17
.handle { case AuthorizationFailedRejection =>
18
 complete(HttpResponse(BadRequest,   entity = HttpEntity(ContentTypes.`application/json`, Json.stringify(
19
   Json.toJson(
20
     ErrorResponse(
21
       BadRequest.intValue, "AuthorizationFailedRejection", "You have entered invalid credentials")
22
   )
23
 )
24
 )))
25
}
26
 
          
23
 )



If we hit the path with the right credential values, such as “localhost:8080/login?username = knoldus&password = pune,” we will get a response as:

Java




xxxxxxxxxx
1


1
{
2
   "code": 200,
3
   "message": "Login Successful"
4
}



When we try to provide access with the wrong values, like “ localhost:8080/login?username = software&password = abcd, ” we will get a response as:

Java




xxxxxxxxxx
1


1
{
2
   "code": 400,
3
   "message": "AuthorizationFailedRejection",
4
   "reason": "You have entered invalid credentials"
5
}



In conclusion, this is how RejectionHandler can be customized to handle errors in code. You can view the entire code on GitHub.

Thanks for reading! Happy coding!

This article was originally published on the Knoldus blog.

Further Reading

Rejection Handling in Akka HTTP

Error Handling Strategies

Topics:
akka http ,rejection handling ,rejection ,java ,scala

Published at DZone with permission of Praful Bangar . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}