Library that transforms exceptions to JSON API error objects for Spring Boot applications
❗ This library would no longer be maintained and would be superseeded by the json-api |
---|
implementation("de.ebf:spring-json-api-errors:0.0.5")
To enable JSON API Exception processing you would need to add the following annotation to your application
@EnableJsonApiErrors
@SpringBootApplication
public class MySpringApplication {
public static void main(String[] args) {
SpringApplication.run(MySpringApplication.class, args);
}
}
Then in your controllers you can add an exception handler that would return the JSON API Errors response:
@RestController
public class Controller {
@Autowired
private JsonApiErrorsBuilder builder;
@ExceptionHandler(Exception.class)
public ResponseEntity<JsonApiErrors> handle(Exception e) {
return builder.build(e);
}
}
As you can see the EnableJsonApiErrors
annoation would register a few Spring Beans in your application context. Here are the two which are the most important ones:
- JsonApiErrorsBuilder
- JsonApiErrorsWriter
JsonApiErrorsBuilder
bean is used to construct the JsonApiErrors
response entity out of a Throwable
instance.
It uses a chain of ExceptionResolver
implementations to resolve the caught exception that would be translated using the
configured ErrorMessageSource
.
You can customize the JsonApiErrorsBuilder
using the JsonApiErrorConfigurer
. Here is a quick example on how you can use the configurer:
@Slf4j
@Configiration
@EnableJsonApiErrors
public class JsonApiErrorConfiguration implements JsonApiErrorConfigurer {
@Override
public void configure(@NonNull JsonApiErrorsBuilderFactory factory) {
factory
.withErrorLogger(JsonApiErrorConfiguration::errorLogger) // add your custom error logger
.withExceptionResolver(new MyExceptionResolver()) // add your custom exception resolver
.withMessageBundles("messages/json-api-errors"); // add message bundle locations that would be used for translations
}
@Override
public void configure(@NonNull ErrorMappingRegistry registry) {
// you can also register your custom exception types with an error and status code
registry.register(TenantCreationFailedException.class)
.code("errors.inventory.tenant-creation-failed")
.status(HttpStatus.INTERNAL_SERVER_ERROR);
}
private static void errorLogger(Throwable e) {
log.error("An exception occurred while handling request", e);
}
private static MyExceptionResolver implements ExceptionResolver {
public Integer getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
public @Nullable ResolvedException resolve(Throwable e) {
/// your custom exception resolver logic
return new ResolvedException(HttpStatus.METHOD_NOT_ALLOWED, HttpHeaders.EMPTY, List.of(
new ErrorMessageResolvable("not-found-message")
));
}
}
}
This service is responsible for writing the JsonApiErrors
that are created by the JsonApiErrorsBuilder
. This Bean is mainly used outside of Spring Controllers, like in Spring Security Exception Handlers, where the servuce needs to render the response manually.
Here is a quick implementation of Spring Security AccessDeniedHandler
interface:
class JsonApiErrorsAccessDeniedHandler implements AccessDeniedHandler {
@Autowired
private JsonApiErrorsBuilder builder;
@Autowired
private JsonApiErrorsWriter writer;
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
writer.write(request, response, builder.build(e));
}
}
JSON API Errors comes with a list of default error resolver implementations.
Implementation that uses Spring's ResponseStatus
annotation to create a ResolvedException
by reading the configured HTTP status code and a reason text from the annoation. Reason Text is used a message code for the MessageSource
service.
@ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR, reason="my-message-code")
class MyException extends RuntimeException {
}
With this resolver you can use the Resolvable
interface in your exceptions to for fine grained configuration.
class MyException extends RuntimeException implements Resolvable {
...
}
or use the JsonApiResolvableException
as a base class where the supplied message would be used a fallback error message in case the errors.my-exception.message
or errors.my-exception
is not present in the defined message sources.
class MyException extends JsonApiResolvableException {
public MyException(String message) {
super("errors.my-exception", message);
}
}
If you wish to supply dynamic arguments for message interpolation you can define custom arguments for your exception like so:
class MyArgumentException extends JsonApiResolvableException {
private final Object[] arguments;
public MyArgumentException(String message, Object argument) {
super("errors.my-exception-with.argument", message);
this.arguments = new Object[] { argument };
}
@Override
public Object[] getArguments() {
return arguments;
}
}
Then your properties file you can add something like this:
errors.my-exception-with.argument.message=Something went wrong with {0}
Resolver that reads the configuration of exception mappings that were added by the JsonApiErrorConfigurer
.
@Configiration
@EnableJsonApiErrors
public class JsonApiErrorConfiguration implements JsonApiErrorConfigurer {
@Override
public void configure(@NonNull ErrorMappingRegistry registry) {
registry.register(MyException.class).code("my-message-code").status(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
This resolver implementation handles the following exception types:
- MethodArgumentNotValidException
- ConstraintViolationException
- BindingResult
Handles various built in Spring and Servlet exception types:
- HttpRequestMethodNotSupportedException
- HttpMediaTypeNotSupportedException
- HttpMediaTypeNotAcceptableException
- MissingServletRequestPartException
- MissingServletRequestParameterException
- MethodArgumentTypeMismatchException
- MissingRequestHeaderException
- MissingRequestCookieException
- MissingMatrixVariableException
- NoHandlerFoundException
This library depends on Spring's MessageSource
to translate exception messages to the client. Each exception that is resolved by the ExceptionResolver
returns a list of Resolvable
instances. This type contains a unique exception error code that is used to find an apropriate message using the MessageSource
. Along with the code it can contain additional arguments for message formatting, a default error message and additional error information.