Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
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.
- Host: GitHub
- URL: https://github.com/jirutka/spring-rest-exception-handler
- Owner: jirutka
- License: apache-2.0
- Created: 2014-04-02T19:23:37.000Z (over 10 years ago)
- Default Branch: master
- Last Pushed: 2024-06-02T08:57:04.000Z (5 months ago)
- Last Synced: 2024-09-29T08:21:46.852Z (about 1 month ago)
- Topics: exception-handler, groovy, java, rest, spring-mvc, springframework
- Language: Java
- Homepage:
- Size: 174 KB
- Stars: 355
- Watchers: 37
- Forks: 129
- Open Issues: 15
-
Metadata Files:
- Readme: README.adoc
- Changelog: CHANGELOG.adoc
- License: LICENSE
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/springframeworkifdef::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].