Rechercher
 

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 :

Validation d'un formulaire JSF par annotations

Validation d'un formulaire JSF Gestion des événements JSF



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

Introduction

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.

l'acronyme JSR correspond à « Java Specification Request ». On retrouve l'ensemble des JSR sur le site http://jcp.org : le « Java Community Process ».

Définition d'une annotation de validation

La définition d'une annotation de validation passe par deux étapes :

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.

Définition de l'annotation à proprement parler

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 {};
}
Définition de l'annotion de validation

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.

Définition d'un validateur associé à l'annotation de validation

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();
    }
    
}
Implémentation de la classe de validation

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.

une expression régulière doit être transformée en une machine à état et cette transformation coûte un certains temps. Afin d'améliorer les performances, il peut être utile de réaliser cette transformation une fois pour toute en « compilant » l'expression régulière. C'est ce qui est fait en ligne 10 de l'exemple proposé (notez bien le qualificateur static utilisé).

Utilisation d'annotations de validation

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.

Utilisation de notre annotation

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 !" /> &#160;
                <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" /> &#160;
                <h:message for="password" styleClass="errorBlock" />
                <br/><br/>
                
                <h:commandButton action="#{loginBean.returnAction}" value="Connect" />
            </h:form>
        </body>
    </f:view>
</html>
Définition du formulaire de saisie
il pourrait être envisagé que les attributs 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";
    }
    
}
La classe de backing bean
en ligne 22, une autre annotation de validation est utilisée : elle permet de vérifier la longueur du mot de passe saisie. Cette annotation est proposée par l'API Bean Validation et nous allons en présenter quelques autres dans la section suivante.

Voici la page web produite par JSF si les données saisies ne respectent pas les bons formats.

le message d'erreur affiché pour le champ « Login » est spécifié dans votre classe d'annotation de validation (valeur par défaut de la propriété 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.

Les annotations proposées par l'API Bean Validation

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.



Validation d'un formulaire JSF Gestion des événements JSF