javaspringspring-bootbean-validation

Custom constraintvalidator in spring boot app does not invoked


I am trying to create custom validator for some class I defined constraint as following ( according to different samples , for example from here )

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { MyClassValidator.class })
@Documented
public @interface MyClassValid {
    String message() default "The password2 does not comply with the rules";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

Then I created corresponding validator :

 public class MyClassValidator implements ConstraintValidator<MyClassValid, ChangePasswordBody> {
        @Override
        public boolean isValid(ChangePasswordBody value, ConstraintValidatorContext context) {
            
/*This check only for sample . Ot can be replaced by other logic*/
if (value.getNewPassword().length() > 2 ) {
                return true;
            }
            return false;
        }
    }

Class to be validated :

@MyClassValid
public class ChangePasswordBody {
    private String oldPassword;
    private String newPassword;

    public String getOldPassword() {
        return oldPassword;
    }

    public void setOldPassword(String oldPassword) {
        this.oldPassword = oldPassword;
    }

    public String getNewPassword() {
        return newPassword;
    }
    
    public void setNewPassword(String newPassword) {
        this.newPassword = newPassword;
    }
}

This is code using ChangePasswordBody

private static void testChangePwd(ApplicationContext applicationContext) {
    //...
    ChangePasswordBody pwdBody = new ChangePasswordBody();
    isValidPassword(pwdBody);
    //...
}
private static void isValidPassword(ChangePasswordBody pwdBody) {
    pwdBody.setNewPassword("l"); 
}

I see that when executing testChangePwd method - MyClassValidator.isValid is not invoked. Is there something missing / wrong in the code ? Thanks in advance


Solution

  • The validation will not take place automatically , you have to somehow invoke it using the bean validation API (e.g. Validator)

    Presume spring-boot already auto-configures a Validator , in your case you could trigger the validation programatically by :

    private static void testChangePwd(ApplicationContext applicationContext) {
        
        ChangePasswordBody pwdBody = new ChangePasswordBody();
        pwdBody.setNewPassword("l");  
    
        Validator validator = applicationContext.getBean(Validator.class);
        Set<ConstraintViolation<ChangePasswordBody>> validationErrors =  validator.validate(pwdBody);
    
        //handle the validationErrors here...
    }
    

    But it is a very bad idea that it requires to get a bean from the spring context in the business codes as it would couple it to spring . A much better approach is to make the object that you want to validate as the method argument and validate it through executing that method.

    Something likes :

    @Validated
    public class PasswordService {
    
        public void changePassword(@Valid ChangePasswordBody request) {
    
        }
    }
    
    

    And if ChangePasswordBody fails the validation , it will throw ConstraintViolationException.