Reputation: 533
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
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
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
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
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:
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
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