Bean Validation - Homepage
Hibernate validator - Doc
Spring Framework - Java Bean Validation
Spring Boot - Validation
Ajouter le starter pour récupérer jakarta-validation et hibernate-validator :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>Valider les paramètres et valeur de retour d'une méthode. A noter la présence de @Validated sur la classe.
@Service
@Validated
public class ValidatedService {
public String findByCodeAndAuthor(@Size(min = 8, max = 10) String code, String author) {
...
}
}
validatedService.findByCodeAndAuthor("12345678", "Clément"); // OK
validatedService.findByCodeAndAuthor("123456", "Clément"); // KO, jakarta.validation.ConstraintViolationException: findByCodeAndAuthor.code: la taille doit être comprise entre 8 et 10class Author {
@NotNull
String name;
@NotNull
String book;
public Author(String name, String book) {
this.name = name;
this.book = book;
}
}
@Autowired
Validator validator;
Set<ConstraintViolation<Author>> violations = validator.validate(new Author(null, "XYZ"));
log.info("violations : {}", violations); // violations : [ConstraintViolationImpl{interpolatedMessage='ne doit pas être nul', propertyPath=name, rootBeanClass=class com.example.spring.batch.SpringBatchApplication$Author, messageTemplate='{jakarta.validation.constraints.NotNull.message}'}]
log.info("violation.message : {}", violations.iterator().next().getMessage()); // violation.message : ne doit pas être nulLe basename du resourceBundle est ValidationMessages par défaut.
Certains messages ne sont pas dynamiques, eg. @NotNull. Une alternative est de personnaliser le message tout en gardant la traduction par défaut :
@NotNull(message = "L'auteur {jakarta.validation.constraints.NotNull.message}")
String name;Pour changer le resourceBundle par défaut, eg. par messages.properties qui est le choix par défaut dans Spring Boot :
@Bean
public Validator getValidator(MessageSource messageSource) {
LocalValidatorFactoryBean factory = new LocalValidatorFactoryBean();
factory.setValidationMessageSource(messageSource);
return factory;
}Jakarta Bean Validation - Default message interpolation algorithm
Jakarta Bean Validation - Appendix B: Standard ResourceBundle messages
Spring Framework - LocalValidatorFactoryBean.setValidationMessageSource(MessageSource) (javadoc)
Avant de créer sa propre Constraint, vérifier qu'elle n'existe pas déjà dans :
- Hibernate validator additional constraints : https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#validator-defineconstraints-hv-constraints
- https://github.com/nomemory/java-bean-validation-extension
- ou via une composition : https://jakarta.ee/specifications/bean-validation/3.0/jakarta-bean-validation-spec-3.0.html#constraintsdefinitionimplementation-constraintcomposition
Exemple sur un bean :
@Documented
@Constraint(validatedBy = AllNotNullValidator.class)
@Target({ TYPE })
@Retention(RUNTIME)
@interface AllNotNull {
String message() default "{com.acme.constraint.MyConstraint.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
class AllNotNullValidator implements ConstraintValidator<AllNotNull, Author> {
@Override
public boolean isValid(Author value, ConstraintValidatorContext context) {
return value.name != null && value.favoriteManga != null;
}
}
@AllNotNull
record Author (String name, String favoriteManga) {
}Lecture utile :
@SpringJUnitConfig({LocalValidatorFactoryBean.class})
public class ValidatorTest {
@Autowired
private Validator validator;
@Test
void checkXXX() {
...
}
}