In Spring you can set up "global" exception handler via @ControllerAdvice and @ExceptionHandler annotation. I'm trying to utilize this mechanism to have two global exception handlers:
RestControllerExceptionHandler
- which should return error responses as json for any controller annotated with @RestController
ControllerExceptionHandler
- which should print error message to the screen for any other controller (annottated with @Controller
)The problem is that when I declare these two exception handlers spring always uses the ControllerExceptionHandler
and never RestControllerExceptionHandler
to handle the exception.
How to make this work ? BTW: I tried to use @Order annotation but this does not seem to work.
Here are my exception handlers:
// should handle all exception for classes annotated with
@ControllerAdvice(annotations = RestController.class)
public class RestControllerExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleUnexpectedException(Exception e) {
// below object should be serialized to json
ErrorResponse errorResponse = new ErrorResponse("asdasd");
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
// should handle exceptions for all the other controllers
@ControllerAdvice(annotations = Controller.class)
public class ControllerExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleUnexpectedException(Exception e) {
return new ResponseEntity<String>("Unexpected exception, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
When I remove ControllerExceptionHandler
than RestControllerExceptionHandler
is correctly called by spring (only for classes annotated with @RestController
).... but when I add ControllerExceptionHandler
than all goes via ControllerExceptionHandler
. Why?
In Java, exception handling is done by try, catch blocks but spring boot also allows us to provide customized global exception handling where we need not to add try catch block everwhere, we can create a separate class for handling exceptions and it also separates the exception handling code from businesss logic code.
Spring MVC provides exception handling for your web application to make sure you are sending your own exception page instead of the server-generated exception to the user. The @ExceptionHandler annotation is used to detect certain runtime exceptions and send responses according to the exception.
Controller Based - We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes Exception class as argument.
After some deeper investigation it seems that the alphabetical order matters :/.
When I renamed my RestControllerExceptionHandler
to ARestControllerExceptionHandler
(which alphabetically precedes ControllerExceptionHandler
) all works as expected! ARestControllerExceptionHandler
correctly handles exceptions from RestControllers
and ControllerExceptionHandler
handles exception from other controllers.
I created a bug in spring for this: https://jira.spring.io/browse/SPR-15432
--- EDIT:
I received the answer for SPR-15432 where it is suggested that this case can be solved with @Order
(org.springframework.core.annotation.Order
) annotation or by implementing Ordered interface.
This did not work for me before, but it seems that I have imported wrong @Order
annotation. (from log4j2 instead of spring). After fixing this it works. Fixed version is following:
// should handle all exception for classes annotated with
@ControllerAdvice(annotations = RestController.class)
@Order(1) // NOTE: order 1 here
public class RestControllerExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleUnexpectedException(Exception e) {
// below object should be serialized to json
ErrorResponse errorResponse = new ErrorResponse("asdasd");
return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// should handle exceptions for all the other controllers
@ControllerAdvice(annotations = Controller.class)
@Order(2) // NOTE: order 2 here
public class ControllerExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleUnexpectedException(Exception e) {
return new ResponseEntity<String>("Unexpected exception, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With