Search code examples
javaspring-bootspring-annotations

Springboot custom validation jsr 303 is not working


I was trying to add custom annotation validation on the age field. as I saw some JSR 303 topics

Controller

@Validated
@Controller
public class StudentController {

    @Autowired
    private StudentRepository repository;

    @PostMapping("/send")
    public ResponseEntity saveStudent(@AgeValidator @RequestBody Student student) {
        System.out.println("saveStudent invoked");
        repository.save(student);
        ResponseData responseData = new ResponseData();
        responseData.setResultId("result");
        responseData.setResultValue("saved");
        ResponseEntity entity = new ResponseEntity(responseData, HttpStatus.OK);
        return entity;
    }
}

Model

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    @Id
    @Column(length = 7)
    private String Id;
    private String fullName;
    private Integer age;
    private String department;

}

AgeValidator

package com.example.demo4;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = AgeValidatorImpl.class)
public @interface AgeValidator {
    String message()
            default "Please enter a valid age";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

AgeValidatorImpl

class AgeValidatorImpl implements ConstraintValidator<AgeValidator, Student> {

    @Override
    public void initialize(AgeValidator constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public boolean isValid(Student t, ConstraintValidatorContext cvc) {
        System.out.println("AgeValidatorImpl invoked");
        if (t.getAge() < 18) {
            return false;
        } else if (t.getAge() > 40) {
            return false;
        }
        return true;
    }
}

so if am sending using postman at any age it saves the record and it's not validating. I saw many peoples commented to add annotation on controller @validated which I import from import org.springframework.validation.annotation.Validated. Still Why this is not working. what am missing?


Solution

  • You can try this,

    I have updated @Age annotation. You can provide upper and lower limit for validation. Note I that have added ElementType.FIELD to @Target. It allows you to use this in class fields as well.

    @Documented
    @Target({ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = AgeValidator.class)
    public @interface Age {
        int lower() default 14;
        int upper() default 60;
        String message() default "Please enter a valid age";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    

    This is the validation constrain for the annotation.

    public class AgeValidator implements ConstraintValidator<Age, Integer> {
    
        private int upperLimit;
        private int lowerLimit;
    
        @Override
        public boolean isValid(Integer i, ConstraintValidatorContext constraintValidatorContext) {
            return lowerLimit < i && i < upperLimit;
        }
    
        @Override
        public void initialize(Age constraintAnnotation) {
            this.lowerLimit = constraintAnnotation.lower();
            this.upperLimit = constraintAnnotation.upper();
        }
    }
    

    You can pass the annotation to class fields and override the upper and lower limit.

    @Entity
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student {
    
        @Id
        @Column(length = 7)
        private String Id;
        private String fullName;
        @Age(lower = 10, upper = 70)
        private Integer age;
        private String department;
    
    }
    

    Used @Validated annotation to validate the Student object against all the validation constraints.

    @PostMapping("/send")
        public ResponseEntity saveStudent(@Validated @RequestBody Student student)
    

    Update

    Replace this dependency,

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
        <version>2.0.1.Final</version>
        <type>jar</type>
    </dependency>
    

    by this

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>