When creating a REST controller with Spring Boot it's possible to use Bean Validation to validate the methods arguments annotating the class with @Validated.

Then Bean Validation annotations can be used directly on the argument or, if the argument is a complex class with properties that must be validated (such as the request body), using @Valid.

The inconsistency I found was while handling violation on those validations.
While using the annotations from javax.validation.constraints (the validations from the Bean Validation API) the violations are handled by the @Validated handler (MethodValidationInterceptor) and throws ConstraintViolationException.
When using @Valid the violation is handled by ModelAttributeMethodProcessor and throws MethodArgumentNotValidException.

See the problem?

The @Validated, a Spring annotation that says it is a "Variant of JSR-303's Valid" and "Designed for convenient use with Spring's JSR-303 support but not JSR-303 specific" (see it here), throws ConstraintViolationException, an exception from the Bean Validation API (JSR-303).
And the opposite also is true, the @Valid (from Bean Validation API) throws MethodArgumentNotValidException (Spring exception).

I think it would be more concise if their behavior changed between them.
Does it makes sense?

Comment From: philwebb

I've transferred this issue to the spring-framework team since the code in question is part of spring-webmvc.

Comment From: ah1508

There is also the BindException, thrown by spring mvc if the invalid object has been built from the request parameters.

exemple : GET /books?title=&author.lastname= calls a method which declares a @Valid Book parameter (query by example),where title property is @NotBlank.

This forces to handle BindException and MethodNotValidArgumentException, but the code is the same. For example :

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> onMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    return e.getBindingResult().getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
}

@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> onBindException(BindException e)  {
    return e.getBindingResult().getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
}

a @ValidationFailureHandler could help to unify these common behaviors.

Comment From: sbrannen

@ah1508,

This forces to handle BindException and MethodNotValidArgumentException, but the code is the same. For example :

I was going to say you can handle both exceptions at the same time as follows...

@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> onValidationFailureException(Exception ex) {
    return ((CommonBindingResultInterface) ex).getBindingResult().getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage));
}

... but then I realized that MethodArgumentNotValidException and BindException do not share a common super type that declares the getBindingResult() method.

So one option to make this easier would be to introduce a common interface (like the CommonBindingResultInterface in the above example) that declares BindingResult getBindingResult(), and then MethodArgumentNotValidException and BindException could both implement that interface.

Comment From: rstoyanchev

@rafafael03 I've read your description several times and I can't be sure I understand what you mean and there are claims that aren't true.

First you can use either @Valid or @Validated interchangeably. They result in the same handling and do not raise different exceptions. The main difference for Spring MVC applications is that @Validated allows you to specify validation groups (hints). Also ModelAttributeMethodProcessor does not and throw MethodArgumentNotValidException but BindException.

What @ah1508 has written is correct. Validation through either of these annotations is supported for @ModelAttribute and for @RequestBody arguments. The former raises BindException but only if you don't handle it in the controller method by declaring a BindingResult argument. The latter raises MethodArgumentNotValidException.

Is this the same as your point?

As for finding a way to consolidate, perhaps MethodArgumentNotValidException could be made to extend BindException. They both have the same internal state more or less.

Comment From: ah1508

Indeed, if MethodArgumentNotValidException extends BindException, only one @ExceptionHandler would be needed.

Comment From: sbrannen

Tentatively slated for 5.3 for pending design work and assessment of viability regarding the proposal to have MethodArgumentNotValidException extend BindException.