Classes with the @Controller
annotation and @ControllerAdvice
can have methods marked with
the @ExceptionHandler
annotation to handle exceptions from controller methods, as shown in the
following example:
@Controller
public class SimpleController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
@Controller
class SimpleController {
// ...
@ExceptionHandler
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
}
The exception can be a propagated high-level exception (for example, a directly thrown
IOException
)
or a nested cause inside a wrapper exception (for example, IOException
wrapped inside an IllegalStateException
).
As of 5.3, this can match at arbitrary cause levels, whereas previously only the immediate cause was taken into
account.
For matching exception types, it is preferable to declare the target exception as a method argument,
as shown in the previous example. When multiple exclusion methods match, a root exclusion match is generally
preferred over a cause exclusion match. In particular, the ExceptionDepthComparator
is used to sort
exceptions based on their depth of the type of exception thrown.
Additionally, an annotation declaration can narrow down the types of exceptions that must match, as shown in the following example :
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
// ...
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
You can even use a list of specific exception types with a very general argument signature, as shown in the following example:
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
// ...
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handle(ex: Exception): ResponseEntity<String> {
// ...
}
The difference between root and cause exception mapping can surprise.
In the IOException
variant shown earlier, the method is typically called using the actual FileSystemException
or RemoteException
instance as an argument, since both of them extend from IOException
.
However, if any such matching exception is propagated within a wrapper exception that is itself an IOException
,
then the passed exception instance is that wrapper exception.
In the handle(Exception)
option the operating logic is even simpler. It is called using a wrapper
exception in the wrapping script, while the actual corresponding exception can be found via
ex.getCause()
in this case. The exception passed is an actual instance of
FileSystemException
or RemoteException
only if they are thrown as high-level
exceptions.
It is generally recommended to give as many be more specific in the argument signature
to reduce the likelihood of inconsistency between root and cause exception types. Consider the possibility of
breaking a multi-hit method into separate methods annotated with @ExceptionHandler
, each of which
matches one specific type of exception through its signature.
In a configuration with multiple @ControllerAdvice
annotations we recommend declaring root exception mappings in the
@ControllerAdvice
annotation in appropriate priority order. Although root exception matching is
preferred over cause exception matching, the definition occurs among the methods of a given controller or class
annotated with @ControllerAdvice
. This means that a cause match for a bean annotated with @ControllerAdvice
with a higher priority is preferred over any match (such as root) for a bean annotated with
@ControllerAdvice
with a lower priority.
Last but not least, an implementation of a method
marked with the @ExceptionHandler
annotation may refuse to handle a given exception instance by
re-throwing it in its original form. This is useful in cases where you are only interested in matches at the root
level, or matches in a specific context that cannot be determined statically. Re-throwing of the exception
propagates along the remaining resolution chain as if the given method with the @ExceptionHandler
annotation did not match the first one.
Support for methods with the @ExceptionHandler
annotation in Spring MVC it is built at the DispatcherServlet
level, the HandlerExceptionResolver
mechanism.
Method arguments
Methods with the @ExceptionHandler
annotation support the
following arguments:
Method argument | Description |
---|---|
Exception type |
Provides access to the exception thrown. |
|
Provides access to the method controller that threw the exception. |
|
Typed access to request parameters and request and session attributes without direct use of the Servlet API. |
|
Select any specific request or response type (for example, |
|
Ensures the presence of a session. As a consequence, such an argument is never |
|
The current authenticated user – possibly a concrete implementation class of |
|
HTTP request method. |
|
The current locale of the request, determined by the most specific |
|
The time zone associated with the current request, as determined by the
|
|
Provides access to the raw response body, as this is provided by the Servlet API. |
|
Provides access to the model to provide an error response. Always empty. |
|
Set the attributes used in the case of a redirect - (which will be added to the request string) and flash attributes for temporary storage until the request arrives after the redirect. |
|
Designed to provide access to any session attribute, as opposed to model attributes, stored in the
session as a result of declaring the |
|
Designed to provide access to request attributes. |
Return values
Methods with the @ExceptionHandler
annotation support the following
return values:
Return value | Description |
---|---|
|
The return value is converted through |
|
The return value specifies that the full response (including HTTP headers and body) will be converted
through |
|
The name of the view that should be recognized by |
|
A |
|
Attributes to add to the implicit model with the view name implicitly defined via |
|
An attribute added to the model with the view name implicitly defined via Note that the |
|
The view and model attributes to use and, if necessary, the response status. |
|
A method with a return type If neither of the above values is true, the return type is |
Any other return value |
If the return value does not match any of the above and is not a prime type (as defined by BeanUtils#isSimpleProperty), by by default it is considered a model attribute that must be added to the model. If it is a simple type, then it remains unresolved. |
Dealing with exceptions in the REST API
A common requirement for services REST is to include error
information in the response body. The Spring Framework does not automatically do this, since the presentation of
error information in the response body is application-specific. However, the @RestController
annotation
can use methods marked with the @ExceptionHandler
annotation with a ResponseEntity
return
value to set the response status and body. Such methods can also be declared in classes annotated with @ControllerAdvice
so that they can be applied globally.
Applications that implement global exception handling with a detailed
error description in the response body should consider extensible ResponseEntityExceptionHandler
, which handles exceptions thrown in Spring
MVC and provides interceptors for customizing the response body. To take advantage of this, create a subclass of
ResponseEntityExceptionHandler
, annotate it with @ControllerAdvice
, override the
necessary methods and declare it as a Spring bean.
GO TO FULL VERSION