To get started, add the following dependency to your Spring Boot project:
<dependency>
<groupId>io.github.wimdeblauwe</groupId>
<artifactId>error-handling-spring-boot-starter</artifactId>
<version>0.3.0</version> (1)
</dependency>
1 |
0.3.0 was the most recent version at the time of writing. See Maven Central for the most current version. |
If we run the first @WebMvcTest
, we now get the following JSON response:
{
"code": "com.wimdeblauwe.examples.errorhandling.inforequest.InfoRequestNotFoundException",
"message": "There is no known info request with id 1"
}
For the validation error, we get this response:
{
"code": "VALIDATION_FAILED",
"message": "Validation failed for object='createInfoRequestRequestBody'. Error count: 3",
"fieldErrors": [
{
"code": "REQUIRED_NOT_BLANK",
"property": "phoneNumber",
"message": "must not be blank",
"rejectedValue": null
},
{
"code": "REQUIRED_NOT_BLANK",
"property": "email",
"message": "must not be blank",
"rejectedValue": null
},
{
"code": "REQUIRED_NOT_BLANK",
"property": "name",
"message": "must not be blank",
"rejectedValue": null
}
]
}
Note how the extra fieldErrors
property is added that lists all the validation problems.
Each of those shows an error code
, the name of the property
for which the validation failed, a human readable error message
and finally the value that was used in the call (rejectedValue
).
As a 3rd example, the linking of support agent with an info request, we get:
{
"code": "com.wimdeblauwe.examples.errorhandling.inforequest.InfoRequestNotFoundException",
"message": "There is no known info request with id 1"
}
We can see that the problem during the linking was the information request. With just the 404 NOT FOUND
response code, we do not have this info.
Running the equivalent @SpringBootTest
tests shows the exact same response.
So, by just including the Error Handling Spring Boot Starter in our project, we get the following improvements over the default Spring Boot error handling:
-
Consistent error responses for @WebMvcTest
and @SpringBootTest
tests.
-
Detailed validation errors per property
-
code
that can be used by machines to react to the error
-
message
that shows a human readable message with detailed info
Customization of the error code
By default, the code
is the full qualified name of the exception that is thrown.
The library uses this as a default since there is little else to go by, but in most cases you will probably want to customize this.
There are 2 ways to do that.
The first way is annotating the exception itself, very similar to how @ResponseStatus
works:
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseErrorCode("INFO_REQUEST_NOT_FOUND") (1)
public class InfoRequestNotFoundException extends RuntimeException {
public InfoRequestNotFoundException(Long id) {
super("There is no known info request with id " + id);
}
}
1 |
Use @ResponseErrorCode to set the value of code that should be used. |
After this change, the error response will be:
{
"code": "INFO_REQUEST_NOT_FOUND",
"message": "There is no known info request with id 1"
}
The other way is to specify this via a property (e.g. in application.properties
).
Start with error.handling.code
, followed by the full qualified name of the exception:
error.handling.codes.com.wimdeblauwe.examples.errorhandling.inforequest.InfoRequestNotFoundException=UNKNOWN_INFO_REQUEST
{
"code": "UNKNOWN_INFO_REQUEST",
"message": "There is no known info request with id 1"
}
Adding additional fields
In some cases, you might want to add additional fields to the error response from the values that are passed to the exception.
Suppose we want to add a property infoRequestId
with the id that could not be found.
To do that, we need to modify the exception class to use @ErrorResponseProperty
:
import io.github.wimdeblauwe.errorhandlingspringbootstarter.ResponseErrorProperty;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class InfoRequestNotFoundException extends RuntimeException {
private final Long infoRequestId;
public InfoRequestNotFoundException(Long id) {
super("There is no known info request with id " + id);
this.infoRequestId = id;
}
@ResponseErrorProperty (1)
public Long getInfoRequestId() {
return infoRequestId;
}
}
1 |
The @ResponseErrorProperty annotation indicates that the result of the method call should be serialized into the error response |
With this in place, the error response becomes:
{
"code": "UNKNOWN_INFO_REQUEST",
"message": "There is no known info request with id 1",
"infoRequestId": 1
}
We can also optionally specify the name of the property that should be used for serialization:
@ResponseErrorProperty("id")
public Long getInfoRequestId() {
return infoRequestId;
}
{
"code": "UNKNOWN_INFO_REQUEST",
"message": "There is no known info request with id 1",
"id": 1
}
In this example, we have put the @ResponseErrorProperty
annotation on a method, but it can also be put on a field to the same effect.