9

Controller method:

public Mission createMission(final @Valid @RequestBody Mission mission) {

    //save..

    return mission;
}

I want to enumerate all validation error messages as json and return.

I can do that with creating a new exception class, adding BindingResult in controller method param, throwing the new exception when binding result has error and handling the exception in @ControllerAdvice class.

if (bindingResult.hasErrors()) {
        throw new InvalidRequestException("Fix input and try again", bindingResult);
    }

But I want to avoid checking BindingResult in each controller method, rather handle globally. I could have done that if I could handle exception: org.springframework.web.bind.MethodArgumentNotValidException. But unfortunately Spring boot does not allow

@ExceptionHandler(MethodArgumentNotValidException.class)

it shows ambiguous exception handler, as the same exception is handled ResponseEntityExceptionHandler, and I can't override handleException as it is final. I tried overriding protected ResponseEntity handleMethodArgumentNotValid() by catching Exception.class, but that does not get called at all.

Is there any way to achieve this goal? it seems like lots of things for a simple task :(

3 Answers 3

11

You should create a class that use @ControllerAdvice and extends ResponseEntityExceptionHandler. Then you can override the method handleMethodArgumentNotValid. You can use something like that:

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

@Override
@ResponseStatus(HttpStatus.BAD_REQUEST)
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
    final List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
    Map <String, Set<String>> errorsMap =  fieldErrors.stream().collect(
            Collectors.groupingBy(FieldError::getField,
                    Collectors.mapping(FieldError::getDefaultMessage, Collectors.toSet())
            )
    );
    return new ResponseEntity(errorsMap.isEmpty()? ex:errorsMap, headers, status);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

If you don't want to extend to ResponseEntityExceptionHandler, you need to annotate your GlobalExceptionHandler to @ControllerAdvice and @ResponseBody.

Update: Spring Boot created a new annotation @RestControllerAdvice in combination of @ControllerAdvice and @ResponseBody.

Comments

0

I found the problem, the controller advice were put in common util module, which does not seem to be used by Spring, though it was under the same base package of component scanning. What I did is overriding methods from ResponseEntityExceptionHandler in a global abstract class (in common module), then in other module just extended that class.

2 Comments

hi @Shafiul ..i put my "@controllerAdvice" class in "com.construction" package and my controller is on "com.construction.controller" package then also i am not able to get error when required param is not present in request
@BhartiLadumor, as I mentioned, it does not matter which package has the controller advice. It needs to be in same module as the controller. You can optimize it by abstracting it in global class and extend in the module containing the controller. Hope it helps

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.