21

When I am validating bean using @Valid annotation in javax.validation, for some objects I am getting ConstraintViolationException and for some I am getting a MethodArgumentNotValidException.

I understand that, if I validate anything in @ResponseBody in the controller , it throws a MethodArgumentNotValidException.

But for some custom validations(eg. @MyCustomValidation) at the class level it is throwing ConstraintViolationException even if it is being validated in @ResponseValidation.

And for some other custom validation for a different REST endpoint, it throws MethodArgumentNotValidException.

I am finding it a bit difficult to understand its 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.

Amal K
  • 4,359
  • 2
  • 22
  • 44
JOHND
  • 2,597
  • 6
  • 27
  • 35
  • can you add the stack trace or fully qualified name of both exceptions – Ryuzaki L Jul 12 '19 at 16:41
  • I checked the root cause in debug mode but it is showing all validated fields in the root cause and the exception is same – JOHND Jul 12 '19 at 17:03
  • 1
    why don't you add it here – Ryuzaki L Jul 12 '19 at 21:34
  • and also add the `ClassA` and `ClassB ` with import statements, there is a possibility of `ConstraintViolationException` which is part of jsr 380 – Ryuzaki L Jul 12 '19 at 21:47
  • Exception strace for : javax.validation.ConstraintViolationException: methodName.obj_name.field_name: must not be blank – JOHND Jul 15 '19 at 18:16
  • Import statements are : – JOHND Jul 15 '19 at 18:18
  • import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; – JOHND Jul 15 '19 at 18:18
  • I can not post completed code here – JOHND Jul 15 '19 at 18:18
  • 1
    @Deadpool : One thing I observed is whenever I add validation groups (eg. @SomeValidation(groups=GroupA.class)) I get ConstraintViolationException and If I remove groups I get MethodArgumentNotValidException – JOHND Jul 15 '19 at 18:52

3 Answers3

9

For a simple understanding, if validation happens at the controller/service layer by using the @Valid annotation, it generates MethodArgumentNotValidException. You can add a handler for this and return the response accordingly. This class is part of the Spring framework, and validation is performed by the 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;
    }

Instead, if you do not validate using the @Valid annotation, an exception is raised by Hibernate at the JPA layer and generates ConstraintViolationException. This exception is part of the Javax bean validation framework and raised at the time of performing the 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;
        }
dbreaux
  • 4,982
  • 1
  • 25
  • 64
Hashir Labs
  • 190
  • 2
  • 6
8

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.

Mohamed Sweelam
  • 1,109
  • 8
  • 22
  • 2
    Thank you very much for your quick reply. Even I was under the same assumption but the strange thing is when I move all the validations to service layer, it always throws ConstraintViolationException for all the validations. Not even a single validation throws MethodArgumentNotValidException – JOHND Jul 12 '19 at 16:31
  • As I mentioned in the answer this is thrown from some JPA provider which you are using, Iyou can check that via debugging to see which field exactly is the reason of this? or you can share the code of your model class to see together – Mohamed Sweelam Jul 12 '19 at 16:34
4

Take a look at this article. These exceptions are thrown under different situations when you mix spring's validation framework with bean validation.

Christopher Yang
  • 3,769
  • 4
  • 30
  • 27