Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/jirutka/spring-rest-exception-handler

A convenient Spring MVC exception handler for RESTful APIs.
https://github.com/jirutka/spring-rest-exception-handler

exception-handler groovy java rest spring-mvc springframework

Last synced: about 1 month ago
JSON representation

A convenient Spring MVC exception handler for RESTful APIs.

Awesome Lists containing this project

README

        

= Spring REST Exception handler
:source-language: java
// Project meta
:name: spring-rest-exception-handler
:version: 1.2.0
:group-id: cz.jirutka.spring
:artifact-id: {name}
:gh-name: jirutka/{name}
:gh-branch: master
:codacy-id: ca5dbac87d564725b6640a67b2b7ea35
// URIs
:src-base: link:src/main/java/cz/jirutka/spring/exhandler
:spring-jdoc-uri: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework

ifdef::env-github[]
image:https://travis-ci.org/{gh-name}.svg?branch={gh-branch}["Build Status", link="https://travis-ci.org/{gh-name}"]
image:https://coveralls.io/repos/github/{gh-name}/badge.svg?branch={gh-branch}[Coverage Status, link="https://coveralls.io/github/{gh-name}"]
image:https://api.codacy.com/project/badge/grade/{codacy-id}["Codacy code quality", link="https://www.codacy.com/app/{gh-name}"]
image:https://maven-badges.herokuapp.com/maven-central/{group-id}/{artifact-id}/badge.svg[Maven Central, link="https://maven-badges.herokuapp.com/maven-central/{group-id}/{artifact-id}"]
endif::env-github[]

The aim of this project is to provide a convenient exception handler (resolver) for RESTful APIs that meets a best-practices for error responses without repeating yourself.
It’s very easy to handle custom exceptions, customize error responses and even localize them.
Also solves some pitfalls footnote:[Nothing terrible, Spring MVC is still a far better then JAX-RS for RESTful APIs! ;)] in Spring MVC with a content negotiation when producing error responses.

== Error message

Error messages generated by `ErrorMessageRestExceptionHandler` follows the http://tools.ietf.org/html/draft-nottingham-http-problem-06[Problem Details for HTTP APIs] specification.

For example, the following error message describes a validation exception.

*In JSON format:*

[source, json]
----
{
"type": "http://example.org/errors/validation-failed",
"title": "Validation Failed",
"status": 422,
"detail": "The content you've send contains 2 validation errors.",
"errors": [{
"field": "title",
"message": "must not be empty"
}, {
"field": "quantity",
"rejected": -5,
"message": "must be greater than zero"
}]
}
----

*… or in XML:*

[source,xml]
----

http://example.org/errors/validation-failed
Validation Failed
422
The content you've send contains 2 validation errors.


title
must not be empty


quantity
-5
must be greater than zero

----

== How does it work?

=== RestHandlerExceptionResolver

The core class of this library that resolves all exceptions is {src-base}/RestHandlerExceptionResolver.java[RestHandlerExceptionResolver].
It holds a registry of `RestExceptionHandlers`.

When your controller throws an exception, the `RestHandlerExceptionResolver` will:

. Find an exception handler by the thrown exception type (or its supertype, supertype of the supertype… up to the `Exception` class if no more specific handler is found) and invoke it.
. Find the best matching media type to produce (using {spring-jdoc-uri}/web/accept/ContentNegotiationManager.html[ContentNegotiationManager], utilises _Accept_ header by default). When the requested media type is not supported, then fallback to the configured default media type.
. Write the response.

=== RestExceptionHandler

Implementations of the {src-base}/handlers/RestExceptionHandler.java[RestExceptionHandler] interface are responsible for converting the exception into Spring’s {spring-jdoc-uri}/http/ResponseEntity.html[ResponseEntity] instance that contains a body, headers and a HTTP status code.

The main implementation is {src-base}/handlers/ErrorMessageRestExceptionHandler.java[ErrorMessageRestExceptionHandler] that produces the `ErrorMessage` body (see above for example).
All the attributes (besides status) are loaded from a properties file (see the section <>).
This class also logs the exception (see the <> section).

== Configuration

=== Java-based configuration

[source]
----
@EnableWebMvc
@Configuration
public class RestContextConfig extends WebMvcConfigurerAdapter {

@Override
public void configureHandlerExceptionResolvers(List resolvers) {
resolvers.add( exceptionHandlerExceptionResolver() ); // resolves @ExceptionHandler
resolvers.add( restExceptionResolver() );
}

@Bean
public RestHandlerExceptionResolver restExceptionResolver() {
return RestHandlerExceptionResolver.builder()
.messageSource( httpErrorMessageSource() )
.defaultContentType(MediaType.APPLICATION_JSON)
.addErrorMessageHandler(EmptyResultDataAccessException.class, HttpStatus.NOT_FOUND)
.addHandler(MyException.class, new MyExceptionHandler())
.build();
}

@Bean
public MessageSource httpErrorMessageSource() {
ReloadableResourceBundleMessageSource m = new ReloadableResourceBundleMessageSource();
m.setBasename("classpath:/org/example/messages");
m.setDefaultEncoding("UTF-8");
return m;
}

@Bean
public ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.setMessageConverters(HttpMessageConverterUtils.getDefaultHttpMessageConverters());
return resolver;
}
}
----

=== XML-based configuration

[source, xml]
----
















----

=== Another resolvers

The {spring-jdoc-uri}/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html[ExceptionHandlerExceptionResolver] is used to resolve exceptions through {spring-jdoc-uri}/web/bind/annotation/ExceptionHandler.html[@ExceptionHandler] methods.
It must be registered _before_ the RestHandlerExceptionResolver.
If you don’t have any `@ExceptionHandler` methods, then you can omit the `exceptionHandlerExceptionResolver` bean declaration.

=== Default handlers

Builder and FactoryBean registers a set of the default handlers by default.
This can be disabled by setting `withDefaultHandlers` to false.

=== Localizable error messages

Message values are read from a _properties_ file through the provided {spring-jdoc-uri}/context/MessageSource.html[MessageSource], so it can be simply customized and localized.
Library contains a default link:src/main/resources/cz/jirutka/spring/exhandler/messages.properties[messages.properties] file that is implicitly set as a parent (i.e. fallback) of the provided message source.
This can be disabled by setting `withDefaultMessageSource` to false (on a builder or factory bean).

The key name is prefixed with a fully qualified class name of the Java exception, or `default` for the default value; this is used when no value for a particular exception class exists (even in the parent message source).

Value is a message template that may contain https://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html[SpEL] expressions delimited by `#{` and `}`.
Inside an expression, you can access the exception being handled and the current request (instance of http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html[HttpServletRequest]) under the `ex`, resp. `req` variables.

*For example:*

[source, properties]
----
org.springframework.web.HttpMediaTypeNotAcceptableException.type=http://httpstatus.es/406
org.springframework.web.HttpMediaTypeNotAcceptableException.title=Not Acceptable
org.springframework.web.HttpMediaTypeNotAcceptableException.detail=\
This resource provides #{ex.supportedMediaTypes}, but you've requested #{req.getHeader('Accept')}.
----

=== Exception logging

Exceptions handled with status code 5×× are logged on ERROR level (incl. stack trace), other exceptions are logged on INFO level without a stack trace, or on DEBUG level with a stack trace if enabled.
The logger name is `cz.jirutka.spring.exhandler.handlers.RestExceptionHandler` and a Marker is set to the exception’s full qualified name.

=== Why is 404 bypassing exception handler?

When the {spring-jdoc-uri}/web/servlet/DispatcherServlet.html[DispatcherServlet] is unable to determine a corresponding handler for an incoming HTTP request, it sends 404 directly without bothering to call an exception handler (see http://stackoverflow.com/a/22751886/2217862[on StackOverflow]).
This behaviour can be changed, *since Spring 4.0.0*, using `throwExceptionIfNoHandlerFound` init parameter.
You should set this to true for a consistent error responses.

*When using WebApplicationInitializer:*

[source]
----
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

protected void customizeRegistration(ServletRegistration.Dynamic reg) {
reg.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
...
}
----

*…or classic web.xml:*

[source, xml]
----

rest-dispatcher
org.springframework.web.servlet.DispatcherServlet

throwExceptionIfNoHandlerFound
true

...

----

== How to get it?

Released versions are available in jCenter and the Central Repository.
Just add this artifact to your project:

._Maven_
[source, xml, subs="verbatim, attributes"]
----

{group-id}
{artifact-id}
{version}

----

._Gradle_
[source, groovy, subs="verbatim, attributes"]
compile '{group-id}:{artifact-id}:{version}'

However if you want to use the last snapshot version, you have to add the JFrog OSS repository:

._Maven_
[source, xml]
----

jfrog-oss-snapshot-local
JFrog OSS repository for snapshots
https://oss.jfrog.org/oss-snapshot-local

true

----

._Gradle_
[source, groovy]
----
repositories {
maven {
url 'https://oss.jfrog.org/oss-snapshot-local'
}
}
----

== Requirements

* Spring 3.2.0.RELEASE and newer is supported, but 4.× is highly recommended.
* Jackson 1.× and 2.× are both supported and optional.

== References

* http://www.jayway.com/2012/09/23/improve-your-spring-rest-api-part-ii[Improve Your Spring REST API by M. Severson]
* https://stormpath.com/blog/spring-mvc-rest-exception-handling-best-practices-part-1/[Spring MVC REST Exception Handling Best Practices by L. Hazlewood]
* http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc[Exception Handling in Spring MVC by P. Chapman]
* http://tools.ietf.org/html/draft-nottingham-http-problem-06[IETF draft Problem Details for HTTP APIs by M. Nottingham]

== License

This project is licensed under http://www.apache.org/licenses/LICENSE-2.0.html[Apache License 2.0].