Overview
In the previous part (link to read article here) it was explained how to create custom error messages for the customer rest application in a specific JSON format. This is needed because most of the times the http status is not enough information for the client. Therefore, detailed information must be sent back to the client about the issue.
To achive this, we had to create our own class RestErrorResponse. It is a common requirement for rest apps to provide details in the body of the error response. Here is where Problem Details for HTTP APis specification (Code RFC 7807) comes in handy. its purpose is to define common error formats in http responses, so that apps do not have to define their own. It also can be extended as we will see later in this article.
Spring support for RFC 7807
Support for this specification has been added in Spring 6 and SpringBoot 3. Let's review quickly the primary abstractions:
ProblemDetail: The main class representing a problem detail for RFC 7807. It contains methods to supply problem information like setTitle, setDetails and setType (thess are spec-defined properties). It also has a properties map for additional, non-standard properties.
ErrorResponse: This interface represents a complete error response including status, headers and a ProblemDetail as the body. All Spring MVC exception implement it, meaning
ErrorResponseException: default implementation of ErrorResponse and convinient class for other exception. It can also be used to reduce the number of custom exceptions for some applications.
Without any further ado, let's code some examples
ProblemDetail in Action
We will update the code from part 1 of these series. Let's review the way it works:
The controller throws an exception when a customer is not found.
Then, the Global Advise handles it and return our custom error class.
Let's make some changes to the code. First, the exception will keep customer id in a field.
public class CustomerNotFoundException extends RuntimeException {
private Long id;
public CustomerNotFoundException(Long id) {
super("Customer "+id+" not found!");
this.id = id;
}
public Long getId() {
return id;
}
}
Then, the ExceptionHandler method managing the will be updated as below.
@ExceptionHandler(CustomerNotFoundException.class) ProblemDetail
handleCustomerNotFoundException(CustomerNotFoundException ex) {
ProblemDetail problemDetails = ProblemDetail
.forStatusAndDetail
(HttpStatus.NOT_FOUND,ex.getLocalizedMessage());
problemDetails.setType(URI.create(
"http://localhost:8080/errors/customer-not-found"));
problemDetails.setTitle("Customer Not Found");
// Adding non-standard property
problemDetails.setProperty("customerId", ex.getId());
return problemDetails;
}
Note that both ProblemDetail and ErrorResponse are supported as a return value from @ExceptionHandler methods that render directly to the response (for instance, by being marked @ResponseBody, or declared in an @RestController or RestControllerAdvice class).
Calling the URL http://localhost:8080/api/v1/customers/1000 to get a non-existing customer will return the below data:
{
"type": "http://localhost:8080/errors/customer-not-found",
"title": "Customer Not Found",
"status": 404,
"detail": "Customer 1000 not found!",
"instance": "/api/v1/customers/1000",
"customerId": 1000
}
The spec-defined properties are:
- Type: used to point the client to documentation where it is explained clearly what happened and why.
- Title: just a short headline of the problem.
- Status: the http status code.
- Detail: a description of the problem.
- Instance: The endpoint where the problem was encountered.
That is not all, the content type header returned in the response indicates that there was a problem. This new media type is part of the spec too.
Content-Type: application/problem+json
Plus a non-standard property named "customerId" was added as a element the properties. Spring boot uses the Jackson library, the properties map are automatically converted into JSON (rendered as top-level JSON properties through the ProblemDetailJacksonMixin).
Auto-configure ProblemDetails
ProblemDetails can be auto-configured in Spring MVC to produce custom error messages with the application/problem+json media type. This support can be enabled by setting the property spring.mvc.problemdetails.enabled to true.
This means that enabling this (even without a ControllerAdvice or ExceptionHandlers configured) the response will be RFC 7807 compliant.
For example, lets suppose our @RestControllerAdvice is removed from the project. That is, our REST app does not manage exceptions directly. And now try to hit the endpoint
http://localhost:8080/api/v1/customers/sssss
This will throw a MethodArgumentTypeMismatchException because the method argument is not the expected type. As there is no handler declared for it (or any other parent exception) the response to the client will be:
{
"timestamp": "2023-04-29T23:51:50.291+00:00",
"status": 400,
"error": "Bad Request",
"path": "/api/v1/customers/11"
}
When accessing with a browser Spring retuns the Whitelabel Error Page.
Just by adding the below line to the application.properties file
spring.mvc.problemdetails.enabled=true
will activate problemdetails error responses producing the following json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Failed to convert 'customerId' with value: 'asasa'",
"instance": "/api/v1/customers/asasa"
}
All this with a single configuration line added!
Conclusion
This simple spec is very easy to use in Spring framework. We can standarise error responses for our apps. It can be extended to add custom properties which are appended to the json format.
That is all fort today. Subcribe if you find this content useful as new articles will be published in the comming weeks.
Top comments (0)