Environment-Based Error Handling With Spring Boot and Kotlin
Simplifying debugging in the staging environment
Providing simple but meaningful messages while handling errors in web services should be the preferred approach. This way, we will not expose any internal information about the causes of the errors and we will help the API client properly respond to issues.
When an error occurs, the default Spring Boot behavior is to return a stack trace. The main downside of this approach is that it might leak useful information about our implementation to a potential attacker. On the other hand, stack traces — unlike meaningful messages — are extremely important for debugging.
“Stack traces can tell the developer more about the sequence of events that led to a failure, as opposed to merely the final state of the software when the error occurred. Unfortunately, the same information can be useful to an attacker. The sequence of class names in a stack trace can reveal the structure of the application as well as any internal components it relies on.” — Semmle
The main goal of the approach we are going to present is to automatically adapt error messages according to the current deployment environment. In the staging environment, a simple and meaningful message, as well as a stack trace, will be returned. In the production environment, we will expose only the first one.
Handling Different Deployment Environments
To understand which deployment environment we are in, we will use a custom environment variable called
ENV. We assume that its value is
PRODUCTION in the production environment and
STAGING in the staging environment. In Kotlin, environment variables value can be retrieved as follows:
Defining a Custom Error Class
We are going to define a custom class called
ErrorMessage to represent API errors. The goal of this class is to wrap exceptions in a nice JSON representation to make life easier for API clients.
We can implement such a class as follows:
As you can notice, the
stackTrace attribute is nullable and optional since it will be used in the staging environment only.
Spring Boot Environment-Based Error Handling
@ExceptionHandleris a Spring annotation that provides a mechanism to treat exceptions that are thrown during execution of handlers (Controller operations). This annotation, if used on methods of controller classes, will serve as the entry point for handling exceptions thrown within this controller only. Altogether, the most common way is to use
@ExceptionHandleron methods of
@ControllerAdviceclasses so that the exception handling will be applied globally or to a subset of controllers.” — Toptal
Thanks to them, we can build a global error handling component called
ControllerExceptionHandler. Its goal is to catch exceptions and wrap them in
ErrorMessage objects, which will be serialized into JSON and sent back to API clients. It also implements the environment-based logic, as shown below:
The most important method is
generateResponse, which converts the exception stack trace to a String (as described in this course) and passes it to the
ErrorResponse constructor only when not in the production environment.
This way, the
message attribute can be used to explain what happened to users. In the staging environment, developers can understand why the error occurred thanks to the
This tutorial can be used to build a custom error-handling layer to add to the multi-layered architecture we presented in this article.
Not being able to understand why an error occurred can be frustrating! Exposing detailed error descriptions can be dangerous. There is a way to cautiously face this issue, however. Using the approach above, when an error occurs, users will always see a simple message. But when in the staging environment, developers will have everything they need to debug it.
That’s all, folks! I hope this helps you handle errors in Spring Boot and Kotlin. Let me know if you have any comments or suggestions.