When I am validating bean using @valid annotation in javax.validations, for some objects I am getting ConstraintViolationException and for some I am getting MethodArgumentNotValidException.
I understand that, if I validate anything in @ResponseBody at controller , it throws MethodArgumentNotValidException.
But for some custom validations(eg. @MyCustomValidation) at class level it is throwing ConstraintViolationException even if it is being validated in @ResponseValidation.
And for some other custom validation for different REST endpoint, it throws MethodArgumentNotValidException.
I am finding it bit difficult to understand as it's behavior.
@PostMapping(path = "/someEndPoint")
@Validated(OnASave.class)
public ResponseEntity<ClassA> saveObjA(@Valid @RequestBody ClassA objA)
Result - throws MethodArgumentNotValidException
@PostMapping(path = "/someOtherEndPoint")
@Validated(OnBSave.class)
public ResponseEntity<ClassB> saveObjB(@Valid @RequestBody ClassB objB)
Result - throws ConstraintViolationException
Both ClassA and ClassB has custom validations.
The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.
The @Validated annotation is a class-level annotation that we can use to tell Spring to validate parameters that are passed into a method of the annotated class.
The @Valid annotation will tell spring to go and validate the data passed into the controller by checking to see that the integer numberBetweenOneAndTen is between 1 and 10 inclusive because of those min and max annotations.
The @Valid annotation is a key feature of Bean Validation, as it allows to validate object graphs with a single call to the validator. To make use of it all fields that should be recursively checked should be annotated with @Valid .
for a simple understanding, if validation happens at controller/service layer by using @Valid annotation, it generates MethodArgumentNotValidException, you can add handler for this and return the response accordingly, this class is part of spring framework and validation is performed by spring framework see sample below
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Response> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
logger.info("Invalid arguments found : " + ex.getMessage());
// Get the error messages for invalid fields
List<FieldError> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(fieldError -> new FieldError(fieldError.getField(), fieldError.getDefaultMessage()))
.collect(Collectors.toList());
String message = messageSource.getMessage("invalid.data.message", null, LocaleContextHolder.getLocale());
Response response = new Response(false, message)
.setErrors(errors);
ResponseEntity<Response> responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
return responseEntity;
}
and if you do not validate using @Valid annotation, and exception is raised by hibernate at jpa layer it generates ConstraintViolationException, this exception is part of Javax bean validation framework, and raised at the time of performing persistence operation (before the actual sql execution) see sample below
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Response> handleConstraintViolationException(ConstraintViolationException ex) {
List<FieldError> errors = ex.getConstraintViolations()
.stream()
.map(constraintViolation -> {
return new FieldError(constraintViolation.getRootBeanClass().getName() + " " + constraintViolation.getPropertyPath(), constraintViolation.getMessage());
})
.collect(Collectors.toList());
String message = messageSource.getMessage("invalid.data.message", null, LocaleContextHolder.getLocale());
Response response = new Response(false, message)
.setErrors(errors);
ResponseEntity<Response> responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
return responseEntity;
}
When you use @Valid, you are applying validation which is defined by you on your model class fields, while there are different types of validations, you can choose like @NotNull, @Max, @Min and so on, you will get the matching type.
In general, all of these are parallel to MethodArgumentNotValidException which will be thrown in all cases.
From official document
Exception to be thrown when validation on an argument annotated with @Valid fails.
ConstraintViolationException is thrown by hibernate entity manager when some constrain violated, so this means you violated some fields in some entity you are using.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With