1

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?

4
  • 1
    What is your version of Spring Boot and/or Spring Framework? Post full class (include import). Commented Jul 24, 2022 at 11:03
  • Have you annotated your Controller with @Validated annotation to enable spring to validate controller? Commented Jul 24, 2022 at 12:16
  • @JamesGraham v2.7.1, but the exception is working when it is defined in GlobalExceptionHandler. For this reason, it does not related to version. Maybe completely related to @Order() annotation. Any idea? Commented Jul 24, 2022 at 21:37
  • @acearch Good point, but I had already annotated that. Otherwise it does not work in both exception handling classes. So, it can be fixed using @Order(), but I am not sure if there is a more proper way. Any idea? Commented Jul 24, 2022 at 21:40

1 Answer 1

1

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;

}
Sign up to request clarification or add additional context in comments.

6 Comments

You rock! Voted up ;) Thanks a lot, @Order annotation fixed the problem. However, I encounter some weird situations. Do you have any suggestions for these points? >>>
1. the @Order(Ordered.LOWEST_PRECEDENCE) annotation gives "redundant parameter" warning and I used it as @Order(), but not sure if I have to set using an int e.g. @Order(Integer.MAX_VALUE) (for lowest pritority).
2. On the other hand, I thought that setting only lowest precedence would be enough for my situation. And used the Order() annotation for only GlobalExceptionHandler, but it does not work. So, do I have to use that annotation for ValidationExceptionHandler as well?
3. Should I extend ResponseEntityExceptionHandler for ValidationExceptionHandler also (as I extend in GlobalExceptionHandler)?
@hans #1, is fine to use without parameter, since the default is lowest precedence. #2, since is the default the lowest, is sufficient to annotate the ValidationExceptionHandler with the highest precedence. #3, is not needed, since there are some default handling for exception and these are already in the GlobalExceptionHandler
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.