Shanmugam Thangavelu
Shanmugam Thangavelu

Reputation: 533

Getting Ambiguous @ExceptionHandler method mapped for MethodArgumentNotValidException while startup of spring boot application

I have written custom exception handler class for one of my spring controllers to validate if email attribute from request param is in the proper format. So created a new class which extends ResponseEntityExceptionHandler class and wrote a method with @ExceptionHandler.

But during spring boot application startup, I am getting below exception which is stopping to run my project. Could someone help me to resolve this?

Exception during server startup:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'handlerExceptionResolver' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed;
nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerExceptionResolver]: Factory method 'handlerExceptionResolver' threw exception;
nested exception is java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]: {public com.TestVO com.handler.exception.TestExceptionHandler.methodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception}

Custom Class to handle MethodArgumentNotValidException:

@ControllerAdvice
public class ExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorVO processValidationError(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();
        FieldError fieldError = fieldErrors.get(0);
        ErrorVO dto = new ErrorVO(fieldError.getDefaultMessage());
        return dto;
    }
}

Upvotes: 53

Views: 59403

Answers (5)

Mar-Z
Mar-Z

Reputation: 4868

UPDATE for Spring Boot 3.

If you are extending the ResponseEntityExceptionHandler class and override its methods you should not use the @ExceptionHandler annotation.

Otherwise Spring's org.springframework.web.servlet.HandlerExceptionResolver will find more then one handler for the same exception class and report an error.

This is valid also for methods that just handle those exceptions without overriding handler methods. Even with a different signature and/or in a different class not extending ResponseEntityExceptionHandler. The exceptions are listed here:

@ExceptionHandler({
    HttpRequestMethodNotSupportedException.class,
    HttpMediaTypeNotSupportedException.class,
    HttpMediaTypeNotAcceptableException.class,
    MissingPathVariableException.class,
    MissingServletRequestParameterException.class,
    MissingServletRequestPartException.class,
    ServletRequestBindingException.class,
    MethodArgumentNotValidException.class,
    HandlerMethodValidationException.class,
    NoHandlerFoundException.class,
    NoResourceFoundException.class,
    AsyncRequestTimeoutException.class,
    ErrorResponseException.class,
    MaxUploadSizeExceededException.class,
    ConversionNotSupportedException.class,
    TypeMismatchException.class,
    HttpMessageNotReadableException.class,
    HttpMessageNotWritableException.class,
    MethodValidationException.class,
    BindException.class
})

Solution

Remove @ExceptionHandler annotations from the methods handling the same exceptions as in ResponseEntityExceptionHandler (listed above). For these exceptions you should always override the handler methods and not define your own ones.

Upvotes: 15

smhylc
smhylc

Reputation: 41

I found a solution to this problem!

I changed the @ControllerAdvice annotation to @RestControllerAdvice for Rest controller.

Spring boot and ControllerAdvice structures already contain MethodArgumentNotValidException. Then I override the MethodArgumentNotValidExcetion function that comes by default by calling override method via intellij idea / or a different ide.

and it worked successfully! :) Hope it helps you! :)

Example

@Override
@ResponseStatus
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

    for (ObjectError methodArgumentNotValidException : ex.getBindingResult().getAllErrors()){
        localerrorEntitiesList.add(new ErrorEntity(methodArgumentNotValidException.getDefaultMessage(),methodArgumentNotValidException.getCode()));
    }

    try {
        return new ResponseEntity<>(new ErrorResponse(localerrorEntitiesList),HttpStatus.BAD_REQUEST);
    }catch (Exception e){
        return new ResponseEntity<>(new ErrorResponse(localerrorEntitiesList),HttpStatus.BAD_REQUEST);
    }

}

Upvotes: 1

chaitanya Rudrabhatla
chaitanya Rudrabhatla

Reputation: 1062

The ambiguity is because you have the same method - @ExceptionHandler in both the classes - ResponseEntityExceptionHandler, MethodArgumentNotValidException. You need to write the overridden method as follows to get around this issue -

   @Override
   protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                 HttpHeaders headers, HttpStatus status, WebRequest request) {
          String errorMessage = ex.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
          List<String> validationList = ex.getBindingResult().getFieldErrors().stream().map(fieldError->fieldError.getDefaultMessage()).collect(Collectors.toList());
          LOGGER.info("Validation error list : "+validationList);
          ApiErrorVO apiErrorVO = new ApiErrorVO(errorMessage);
          apiErrorVO.setErrorList(validationList);
          return new ResponseEntity<>(apiErrorVO, status);
   }

Upvotes: 81

akourt
akourt

Reputation: 5573

The problem you experience here is to be expected. You're effectively extending Spring ResponseEntityExceptionHandler which by default already contains a declaration for MethodArgumentNotValidException in it's own @ExceptionHandler annotation.

You can easily check on this from the source of the class:

https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java

This is why you see this bit of error in the logs:

java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]

Based on all the above you should remove your own exception handler method and override the ones from it.

Upvotes: 26

KevKosDev
KevKosDev

Reputation: 859

Spring has a built in ResponseEntityExceptionHandler for MethodArgumentNotValidException. So you have two handlers for the same exception which is not allowed.

You can try to override ResponseEntityExceptionHandler.handleMethodArgumentNotValid().

Upvotes: 10

Related Questions