Search code examples
javaspringspring-bootspring-validatorspring-validation

perform SpringBoot request validation based on configurable property


I would like to use @Valid from SpringBoot to perform request validation on a particular field.

However, I would like the validation on the field to be configurable. I.e, with one configuration, enable the validation or disable it.

Here is the code:

import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
class FieldValidationController {

    @PostMapping("/validate")
    String question(@Valid @RequestBody SomeRequest someRequest) {
        return "please validate the field only if a property is on";
    }

}
import jakarta.validation.constraints.NotNull;

record SomeRequest(

    @NotEmpty
    String alwaysValidateMe,
    
    //How can I validate this property against @NotEmpty based on a property? 
    String onlyVaidateMeIfIamConfigured

) {
}

application.properties

validate.field.onlyVaidateMeIfIamConfigured=true 
#to have the possibility between validate.field.onlyVaidateMeIfIamConfigured=true and validate.field.onlyVaidateMeIfIamConfigured=false

How to perform validation on a field only if a property is on?


Solution

  • To validate a field based on an application property value, it seems that there is no option except creating a custom constraint annotation and a corresponding constraint validator. How it can look like:

    record SomeRequest(
                @NotEmpty(message = "Please enter the title")
                String title,
                @ConditionalNotEmpty(property = "validate.isEmpty") // in properties it will be validate.isEmpty=true
                String string) {
        }
    
    import jakarta.validation.Constraint;
    import jakarta.validation.Payload;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Constraint(validatedBy = ConditionalNotEmptyStringValidator.class)
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ConditionalNotEmpty {
        String property();
        
        String message() default "Field is invalid";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    

    and validator:

    import jakarta.validation.ConstraintValidator;
    import jakarta.validation.ConstraintValidatorContext;
    import org.springframework.core.env.Environment;
    
    public class ConditionalNotEmptyStringValidator implements ConstraintValidator<ConditionalNotEmpty, String> {
    
        private final Environment environment; // autowire to get property value
    
        private String propertyKey;
    
    
        public ConditionalNotEmptyStringValidator(Environment environment) {
            this.environment = environment;
        }
    
        @Override
        public void initialize(ConditionalNotEmpty constraintAnnotation) {
            this.propertyKey = constraintAnnotation.property();
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            boolean isValidationEnabled = Boolean.parseBoolean(environment.getProperty(this.propertyKey, "false")); // find validate.isEmpty value in properties, if not found - false
    
            if (isValidationEnabled) {
                return value != null && !value.isEmpty();
            } else {
                return true;
            }
        }
    }