Kotlin in the backend, Spring with Kotlin Part 2 — Exception Handling
Handling exceptions is a crucial part when developing any sort of application, certain errors occur by calling a method of a library with invalid arguments, others happen just for doing something wrong. However, some exceptions can be thrown by ourselves because what was asked to be done by us is not possible.
In the previous post, we developed a simple Web API using Kotlin and Spring, this API allowed us to request and alter information about books in our system, however, when errors are thrown, the information sent to the API client is not the clearest, who becomes unaware of what really went wrong.
Looking at Figure 1, we can see that there are multiple times when exceptions are thrown, these are:
- Get Specific Book — When the book with the provided ISBN is not in the system;
- Create Book — When a book with the provided ISBN is already in the system.
This information should go to the API’s client in the best way possible, the client should know that an error occurred and what made the error appear.
Figure 2 and 3 shows what is presented to the API’s client when a client tries to obtain a book with an ISBN that is not in the system, and when trying to create a book that’s already registered.
As you can see, we cannot identify what sort of error our request generated, this is not a good way to display errors to the API’s client.
What we will see next is how we can provide more specific errors and how we can define them in our code in an intuitive way.
Exception Handling in Spring MVC
Spring provides multiple ways to handle exceptions, my usual approach is using @ControllerAdvice
together with @ExceptionHandler
.
The best approach to handling exceptions more specifically is defining a class to represent each exception case, in our example, we can define two classes: NoSuchBookException
and BookAlreadyExistsException
. So let’s start with that.
First, we’ll define NoSuchBookException
which takes as a constructor argument the ISBN of the book
Now to define BookAlreadyExistsException
, this won’t take any constructor arguments for now since the client is providing the book identifier (ISBN) in the request body, so he knows that the Book that already exists is the one with that same ISBN.
After defining these classes, we must change our BookService
class so that instead of throwing an exception with a String message, we’ll just throw one of the exceptions that we defined moments ago.
Now that our BookService
has been modified we must define a separate class in which we’ll write the code to handle the exception thrown by our API.
We’ll start by creating the BooksExceptionHandler
class, which is where we’ll handle the errors that have to do with the Book entity.
We’ll add the appropriate annotation(@ControllerAdvice
) to this class, and add a method to handle each type of exception.
To represent an error we created a specific class, called ErrorResponseEntity
, which receives a title and a detailed message. We can create error representations using the createErrorResponseEntity
method which receives the respective error HTTP code and an instance of ErrorResponseEntity
.
Let’s reproduce the same errors as before what see what our Web API returns to the client.
As you can see, when searching for a book that is not in the system, we get an HTTP error code of 404 (Not Found) and an error JSON object which can tell us more about the error.
Now let’s see what happens when we create the same book twice
When creating the same book twice we get an HTTP error code of 400 (Bad request) because we’re trying to create a book with the same ISBN as a book that’s already in the system. We also get a more detailed error JSON object.
With these changes, we handled the exception which was thrown by our code, of course, we should also handle exception which can happen by code that’s not developed by us.
Final considerations
Handling exceptions can be quite tiresome, however, this can help us improve the client experience when using our API, allowing them to understand specific errors, what caused them, and what they did wrong.
This blogpost in spring.io is great to see different ways of handling exceptions.
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
I hope you are enjoying this series of posts about Kotlin in the backend.
My social media info: