Accès rapide :
Introduction
Définition d'une annotation de validation
Définition de l'annotation à proprement parler
Définition d'un validateur associé à l'annotation de validation
Utilisation d'annotations de validation
Utilisation de notre annotation
Les annotations proposées par l'API Bean Validation
Dans le chapitre précédent, nous avons vu que vous pouvez utiliser un certain nombre de tags JSF pour configurer la validation de vos formulaires. Cependant JSF propose une autre manière de valider les données renseignées : utiliser des annotations de validation placées sur les attributs de vos « backing beans ». Ce chapitre du tutoriel Java EE/Jakarta EE explore cette possibilité.
C'est la JSR 303 « Bean Validation » qui définie les modalités de définition
d'annotation de validation. Je vous invite à consulter cette JSR pour de plus amples informations.
Le package principal associé à cette API est javax.validation
.
Notez aussi que ces annotations de validations peuvent être utilisées avec d'autres frameworks que JSF.
La définition d'une annotation de validation passe par deux étapes :
Il faut commencer par coder l'annotation à proprement parler. Pour être une annotation de validation, il faudra respecter certaines règles de codage et notamment le fait d'être associée à une classe Java contenant le code de validation. Ce n'est pas l'annotation qui porte le code de validation.
Il faut ensuite coder la classe de validation, celle-ci étant liée à votre type d'annotations.
Afin de tester cette possibilité, je vous propose de coder une annotation de validation de format d'email. Pour réaliser la vérification, nous utiliserons une expression régulière au niveau de la classe de validation.
L'exemple de code ci-dessous vous montre comment définir l'annotation de validation. Les trois propriétés message
, groups
et
payload
sont requises : si vous les oubliez, des exceptions seront produites au runtime (en cours d'exécution).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package fr.koor.webstore.validator; 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; @Constraint(validatedBy = EmailValidator.class) @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Email { String message() default "Is not a valid email"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } |
Une annotation se définit via le mot clé @interface
et est très fréquemment associée à d'autres annotations utiles à sa définition.
Le lien avec la classe de validation est réalisé en ligne 12 avec l'annotation @Constraint
: la classe de validation se nomme
EmailValidator
.
Passons maintenant au codage de la classe de validation. Celle-ci doit implémenter l'interface ConstraintValidator
. Cette interface définit
deux méthodes : initialize
(c'est une méthode par défaut depuis Java EE 8, il n'est donc plus obligatoire de la redéfinir) et isValid
.
C'est cette dernière méthode qui devra effectuer la validation. Voici un exemple d'implémentation de classe de validation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package fr.koor.webstore.validator; import java.util.regex.Pattern; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class EmailValidator implements ConstraintValidator<Email, String> { private static Pattern emailPattern = Pattern.compile( "^[\\w.-]+@[\\w.-]+\\.[a-z]{2,}$" ); @Override public void initialize( Email constraintAnnotation ) { // Rien à faire pour cet exemple. } @Override public boolean isValid( String email, ConstraintValidatorContext context ) { return emailPattern.matcher( email ).find(); } } |
Comme vous l'avez remarqué, cette classe de validateur utilise une expression régulière pour valider le format de l'email passé en paramètre.
static
utilisé).
Changeons de point de vue et cherchons maintenant à utiliser des annotations de validation. Dans un premier temps nous allons utiliser l'annotation développée ci-dessus. Ensuite nous étudierons d'autres annotations proposées par l'API Bean Validation.
Reprenons le formulaire d'authentification développé dans le chapitre précédent. Retirons-y les tags de validation précédemment utilisés et gardons juste les champs d'affichages d'erreur associés à vos champs de saisie. Voici à quoi devrait ressembler la facelet.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!DOCTYPE html> <html xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:h="http://xmlns.jcp.org/jsf/html"> <f:view> <head> <title>Login screen</title> <link rel="stylesheet" type="text/css" href="../styles.css" /> </head> <body> <h1>Login screen</h1> <h:form id="form"> Login: <h:inputText id="login" value="#{loginBean.login}" required="true" requiredMessage="La saisie du login est obligatoire !" />   <h:message for="login" styleClass="errorBlock" /> <br/> Password: <h:inputSecret id="password" value="#{loginBean.password}" required="true" requiredMessage="La saisie du mot de passe est obligatoire !" validatorMessage="Votre mot de passe doit contenir entre 3 à 12 caractères" />   <h:message for="password" styleClass="errorBlock" /> <br/><br/> <h:commandButton action="#{loginBean.returnAction}" value="Connect" /> </h:form> </body> </f:view> </html> |
required
soient aussi retirés car les annotations que nous allons utiliser imposent de tailles
pour les deux champs à valider. Pour autant, en gardant ces attributs, vous pouvez produire des messages d'erreurs différents en cas de non saisie et
en cas de mauvaise saisie.
Maintenant, il nous faut utiliser quelques annotations pour définir les règles de validation des propriétés de notre backing bean.
Voici le code de la classe LoginBean
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package fr.koor.webstore.ihm; import java.io.Serializable; import javax.enterprise.context.SessionScoped; import javax.inject.Named; import javax.validation.constraints.Size; import fr.koor.webstore.validator.Email; @Named("loginBean") @SessionScoped public class LoginBean implements Serializable { private static final long serialVersionUID = -5433850275008415405L; @Email @Size(min=1) private String login = "james@mi6.uk"; @Size(min=3, max=8) private String password = "007"; public String getLogin() { return login; } public void setLogin(String login) { this.login = login; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String returnAction() { return password.equals( "007" ) ? "success" : "failure"; } } |
Voici la page web produite par JSF si les données saisies ne respectent pas les bons formats.
message
). Il est possible de le changer lors de l'utilisation de l'annotation.
Il est aussi possible de rajouter un attribut validatorMessage
sur le tag de saisie, dans la facelet, pour changer ce message.
Comme nous venons de le voir avec l'annotation @javax.validation.constraints.Size
, il existe d'autres annotations de validation prêtes à l'emploi.
Elles sont localisées dans le package javax.validation.constraints
. En voici quelques-unes.
@AssertFalse : s'applique à un attribut de type booléen et vérifie que sa valeur soit false
.
@AssertTrue : s'applique à un attribut de type booléen et vérifie que sa valeur soit true
.
@DecimalMax("10.5") : permet de vérifier qu'une valeur flottante est bien inférieure ou égale à la valeur spécifiée dans l'annotation de validation. Attention : la valeur spécifiée doit l'être sous forme d'une chaîne de caractères.
@DecimalMin("10.5") : permet de vérifier qu'une valeur flottante est bien supérieure ou égale à la valeur spécifiée dans l'annotation de validation. Attention : la valeur spécifiée doit l'être sous forme d'une chaîne de caractères.
@Digits(integer=3, fraction=2) : permet de vérifier le nombre de chiffres utilisé par une valeur flottante. Vous devez spécifier deux
paramètres entiers dans l'annotation : integer
qui indique le nombre de chiffres utilisé pour la partie entière et fraction
qui indique le nombre de chiffres utilisé par la partie décimale.
@Email : et oui, désolé, mais je vous ai fait coder quelque chose qui existait déjà (il faut bien qu'on apprenne les fondamentaux). Ce annotation de validation demande à vérifier si une chaîne de caractères correspond bien à un email. Attention : ce validateur accepte une chaîne vide contrairement à notre validateur.
@Future : permet de vérifier qu'une date est postérieure à la date actuelle.
@FutureOrPresent : permet de vérifier qu'une date est postérieure ou égale à la date actuelle.
@Max(100) : permet de vérifier qu'un entier est inférieur ou égal à la valeur spécifiée dans l'annotation.
@Min(1) : permet de vérifier qu'un entier est supérieur ou égal à la valeur spécifiée dans l'annotation.
@Negative : permet de vérifier qu'une valeur numérique (entière ou flottante) soit strictement inférieure à zéro.
@NegativeOrZero : permet de vérifier qu'une valeur numérique (entière ou flottante) soit inférieure ou égale à zéro.
@NotBlank : permet de vérifier qu'une chaîne de caractères ne puisse pas être nulle et contienne au moins un caractère qui ne soit pas un séparateur (blanc, tabulation, retour à la ligne, ...).
@NotEmpty : permet de vérifier qu'une chaîne de caractères ne puisse pas être nulle ou vide.
@NotNull : permet de vérifier qu'une valeur ne puisse pas être nulle.
@Null : permet de vérifier qu'une valeur soit nulle.
@Past : permet de vérifier qu'une date est antérieure à la date actuelle.
@PastOrPresent : permet de vérifier qu'une date est antérieure ou égale à la date actuelle.
@Pattern(regExp) : permet de vérifier qu'une chaîne de caractères correspond à l'expression régulière passée en paramètre de l'annotation.
@Positive : permet de vérifier qu'une valeur numérique (entière ou flottante) soit strictement supérieure à zéro.
@PositiveOrZero : permet de vérifier qu'une valeur numérique (entière ou flottante) soit supérieure ou égale à zéro.
@Size(min=3, max=8) : permet de vérifier qu'une chaîne de caractères ait bien sa taille comprise entre une valeur minimale et une valeur maximale.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :