Explicit coverage of root vs cause exception matching in MVC ref docs
Issue: SPR-16743
This commit is contained in:
parent
e4b4d3e2f6
commit
a200df6c8d
|
|
@ -63,8 +63,6 @@ import org.springframework.util.xml.DomUtils;
|
||||||
import org.springframework.web.HttpRequestHandler;
|
import org.springframework.web.HttpRequestHandler;
|
||||||
import org.springframework.web.accept.ContentNegotiationManager;
|
import org.springframework.web.accept.ContentNegotiationManager;
|
||||||
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
|
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
|
||||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||||
import org.springframework.web.method.support.CompositeUriComponentsContributor;
|
import org.springframework.web.method.support.CompositeUriComponentsContributor;
|
||||||
|
|
@ -115,10 +113,10 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
|
||||||
*
|
*
|
||||||
* <p>This class registers the following {@link HandlerExceptionResolver}s:
|
* <p>This class registers the following {@link HandlerExceptionResolver}s:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
|
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
|
||||||
* through @{@link ExceptionHandler} methods.
|
* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
|
||||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
|
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
|
||||||
* with @{@link ResponseStatus}.
|
* with {@link org.springframework.web.bind.annotation.ResponseStatus}.
|
||||||
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
||||||
* exception types
|
* exception types
|
||||||
* </ul>
|
* </ul>
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,6 @@ import org.springframework.validation.Validator;
|
||||||
import org.springframework.web.HttpRequestHandler;
|
import org.springframework.web.HttpRequestHandler;
|
||||||
import org.springframework.web.accept.ContentNegotiationManager;
|
import org.springframework.web.accept.ContentNegotiationManager;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
|
||||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||||
import org.springframework.web.context.ServletContextAware;
|
import org.springframework.web.context.ServletContextAware;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
|
@ -136,10 +134,10 @@ import org.springframework.web.util.UrlPathHelper;
|
||||||
* <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of
|
* <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of
|
||||||
* exception resolvers:
|
* exception resolvers:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
|
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
|
||||||
* through @{@link ExceptionHandler} methods.
|
* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
|
||||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
|
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
|
||||||
* with @{@link ResponseStatus}.
|
* {@link org.springframework.web.bind.annotation.ResponseStatus}.
|
||||||
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
||||||
* exception types
|
* exception types
|
||||||
* </ul>
|
* </ul>
|
||||||
|
|
@ -926,12 +924,11 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||||
* A method available to subclasses for adding default {@link HandlerExceptionResolver}s.
|
* A method available to subclasses for adding default {@link HandlerExceptionResolver}s.
|
||||||
* <p>Adds the following exception resolvers:
|
* <p>Adds the following exception resolvers:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link ExceptionHandlerExceptionResolver}
|
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
|
||||||
* for handling exceptions through @{@link ExceptionHandler} methods.
|
* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
|
||||||
* <li>{@link ResponseStatusExceptionResolver}
|
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
|
||||||
* for exceptions annotated with @{@link ResponseStatus}.
|
* {@link org.springframework.web.bind.annotation.ResponseStatus}.
|
||||||
* <li>{@link DefaultHandlerExceptionResolver}
|
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
|
||||||
* for resolving known Spring exception types
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||||
|
|
|
||||||
|
|
@ -2406,33 +2406,31 @@ controller-specific ``Formatter``'s:
|
||||||
public ResponseEntity<String> handle(IOException ex) {
|
public ResponseEntity<String> handle(IOException ex) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
The annotation can list the exception types to match. Or simply declare the target
|
The exception may match against a top-level exception being propagated (i.e. a direct
|
||||||
exception as a method argument as shown above. When multiple exception methods match,
|
`IOException` thrown), or against the immediate cause within a top-level wrapper exception
|
||||||
a root exception match is generally preferred to a cause exception match. More formally
|
(e.g. an `IOException` wrapped inside an `IllegalStateException`).
|
||||||
the `ExceptionDepthComparator` is used to sort exceptions based on their depth from the
|
|
||||||
thrown exception type.
|
|
||||||
|
|
||||||
In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception
|
For matching exception types, preferably declare the target exception as a method argument
|
||||||
mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root
|
as shown above. Alternatively, the annotation declaration may narrow the exception types to
|
||||||
exception match is preferred to a cause, this is mainly among the methods of a given
|
match. We generally recommend to be as specific as possible in the argument signature and to
|
||||||
controller or `@ControllerAdvice`. That means a cause match on a higher-priority
|
declare your primary root exception mappings on a `@ControllerAdvice` prioritized with a
|
||||||
`@ControllerAdvice` is preferred to any match (e.g. root) on a lower-priority
|
corresponding order. See <<web.adoc#mvc-ann-exceptionhandler,the MVC section>> for details.
|
||||||
`@ControllerAdvice`.
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
An `@ExceptionHandler` method in WebFlux supports the same method arguments and
|
||||||
|
return values as an `@RequestMapping` method, with the exception of request body
|
||||||
|
and `@ModelAttribute` related method arguments.
|
||||||
|
====
|
||||||
|
|
||||||
Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the
|
Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the
|
||||||
`HandlerAdapter` for `@RequestMapping` methods. See <<webflux-dispatcher-exceptions>>
|
`HandlerAdapter` for `@RequestMapping` methods. See <<webflux-dispatcher-exceptions>>
|
||||||
under the `DispatcherHandler` section for more details.
|
under the `DispatcherHandler` section for more details.
|
||||||
|
|
||||||
|
|
||||||
An `@ExceptionHandler` method in WebFlux supports the same method arguments and return
|
|
||||||
values as an `@RequestMapping` method does with the exception of request body and
|
|
||||||
`@ModelAttribute` related method arguments.
|
|
||||||
|
|
||||||
|
|
||||||
[[webflux-ann-rest-exceptions]]
|
[[webflux-ann-rest-exceptions]]
|
||||||
==== REST API exceptions
|
==== REST API exceptions
|
||||||
[.small]#<<web.adoc#mvc-ann-rest-exceptions,Same in Spring MVC>>#
|
[.small]#<<web.adoc#mvc-ann-rest-exceptions,Same in Spring MVC>>#
|
||||||
|
|
|
||||||
|
|
@ -481,7 +481,6 @@ initialization parameters ( `init-param` elements) to the Servlet declaration in
|
||||||
Note that if <<mvc-default-servlet-handler,default servlet handling>> is
|
Note that if <<mvc-default-servlet-handler,default servlet handling>> is
|
||||||
also configured, then unresolved requests are always forwarded to the default servlet
|
also configured, then unresolved requests are always forwarded to the default servlet
|
||||||
and a 404 would never be raised.
|
and a 404 would never be raised.
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2830,22 +2829,75 @@ controller-specific ``Formatter``'s:
|
||||||
public ResponseEntity<String> handle(IOException ex) {
|
public ResponseEntity<String> handle(IOException ex) {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
The annotation can list the exception types to match. Or simply declare the target
|
The exception may match against a top-level exception being propagated (i.e. a direct
|
||||||
exception as a method argument as shown above. When multiple exception methods match,
|
`IOException` thrown), or against the immediate cause within a top-level wrapper exception
|
||||||
a root exception match is generally preferred to a cause exception match. More formally
|
(e.g. an `IOException` wrapped inside an `IllegalStateException`).
|
||||||
the `ExceptionDepthComparator` is used to sort exceptions based on their depth from the
|
|
||||||
thrown exception type.
|
For matching exception types, preferably declare the target exception as a method argument
|
||||||
|
as shown above. When multiple exception methods match, a root exception match is generally
|
||||||
|
preferred to a cause exception match. More specifically, the `ExceptionDepthComparator` is
|
||||||
|
used to sort exceptions based on their depth from the thrown exception type.
|
||||||
|
|
||||||
|
Alternatively, the annotation declaration may narrow the exception types to match:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@ExceptionHandler({FileSystemException.class, RemoteException.class})
|
||||||
|
public ResponseEntity<String> handle(IOException ex) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Or even a list of specific exception types with a very generic argument signature:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@ExceptionHandler({FileSystemException.class, RemoteException.class})
|
||||||
|
public ResponseEntity<String> handle(Exception ex) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
The distinction between root and cause exception matching can be surprising:
|
||||||
|
|
||||||
|
In the `IOException` variant above, the method will typically be called with
|
||||||
|
the actual `FileSystemException` or `RemoteException` instance as the argument
|
||||||
|
since both of them extend from `IOException`. However, if any such matching
|
||||||
|
exception is propagated within a wrapper exception which is an `IOException`
|
||||||
|
itself, the passed-in exception instance will be that wrapper exception.
|
||||||
|
|
||||||
|
The behavior is even simpler in the `handle(Exception)` variant: This will
|
||||||
|
always be invoked with the wrapper exception in a wrapping scenario, with the
|
||||||
|
actually matching exception to be found through `ex.getCause()` in that case.
|
||||||
|
The passed-in exception will only be the actual `FileSystemException` or
|
||||||
|
`RemoteException` instance when these are thrown as top-level exceptions.
|
||||||
|
====
|
||||||
|
|
||||||
|
We generally recommend to be as specific as possible in the argument signature,
|
||||||
|
reducing the potential for mismatches between root and cause exception types.
|
||||||
|
Consider breaking a multi-matching method into individual `@ExceptionHandler`
|
||||||
|
methods, each matching a single specific exception type through its signature.
|
||||||
|
|
||||||
In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception
|
In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception
|
||||||
mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root
|
mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root
|
||||||
exception match is preferred to a cause, this is mainly among the methods of a given
|
exception match is preferred to a cause, this is defined among the methods of a given
|
||||||
controller or `@ControllerAdvice`. That means a cause match on a higher-priority
|
controller or `@ControllerAdvice` class. This means a cause match on a higher-priority
|
||||||
`@ControllerAdvice` is preferred to any match (e.g. root) on a lower-priority
|
`@ControllerAdvice` bean is preferred to any match (e.g. root) on a lower-priority
|
||||||
`@ControllerAdvice`.
|
`@ControllerAdvice` bean.
|
||||||
|
|
||||||
|
Last but not least, an `@ExceptionHandler` method implementation may choose to back
|
||||||
|
out of dealing with a given exception instance by rethrowing it in its original form.
|
||||||
|
This is useful in scenarios where you are only interested in root-level matches or in
|
||||||
|
matches within a specific context that cannot be statically determined. A rethrown
|
||||||
|
exception will be propagated through the remaining resolution chain, just like if
|
||||||
|
the given `@ExceptionHandler` method would not have matched in the first place.
|
||||||
|
|
||||||
Support for `@ExceptionHandler` methods in Spring MVC is built on the `DispatcherServlet`
|
Support for `@ExceptionHandler` methods in Spring MVC is built on the `DispatcherServlet`
|
||||||
level, <<mvc-exceptionhandlers,HandlerExceptionResolver>> mechanism.
|
level, <<mvc-exceptionhandlers,HandlerExceptionResolver>> mechanism.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue