Home > front end >  Spring Boot: Cannot catch exception in @ControllerAdvice
Spring Boot: Cannot catch exception in @ControllerAdvice

Time:07-26

I have 2 exception handler classes annotated with @RestControllerAdvice and:

I use the first one as global exception handler to catch exceptions:

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @Override
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(...) {
      // ...
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseEntity<Object> handleAllUncaughtException(Exception ex, WebRequest request) {
        // ...
    }

    // code omitted for clarity
}

and the second for validation exceptions (I creates custom validation):

@RestControllerAdvice
public class ValidationExceptionHandler { // did not extend from extends ResponseEntityExceptionHandler

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    protected ValidationErrorResponse onConstraintValidationException(ConstraintViolationException e) {
        // ...
    }
}

When I move onConstraintValidationException to GlobalExceptionHandler class, I catch validation exception and display corresponding message. But when it is in the second ControllerAdvice class (ValidationExceptionHandler) as shown above, code does not hit onConstraintValidationException method.

I also tried to extend the second class from ResponseEntityExceptionHandler, but does not make any sense.

So, what is the problem and how can I fix it?

CodePudding user response:

In your case it could be question of priorities... The first exception handler has highest priority, and probably the handleAllUncaughtException is going to capture all the exceptions.

For catching the ConstraintViolationException in the second handler (ValidationExceptionHandler) you should give to it the highest priority, like so:

@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ValidationExceptionHandler { 

and to the first one lowest:

@RestControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

Can have a look at this discussion, it can be interesting to learn more about how it works and have some alternative solutions: Setting Precedence of Multiple @ControllerAdvice @ExceptionHandlers

Edit: in case you need something more flexible or create a more complex hierarchy of handlers, you can use simple integers in the Order annotation. Lowest values, highest priority. So something like this can work as well:

@RestControllerAdvice
@Order(-1)
public class ValidationExceptionHandler { 
//...

@RestControllerAdvice
@Order(0)
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
//...

In the Ordered source code you have:

public interface Ordered {

/**
 * Useful constant for the highest precedence value.
 * @see java.lang.Integer#MIN_VALUE
 */
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

/**
 * Useful constant for the lowest precedence value.
 * @see java.lang.Integer#MAX_VALUE
 */
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

In the Order annotation you can see the default is LOWEST_PRECEDENCE (Integer.MAX_VALUE):

public @interface Order {

/**
 * The order value.
 * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
 * @see Ordered#getOrder()
 */
    int value() default Ordered.LOWEST_PRECEDENCE;

}
  • Related