Accès rapide :
La vidéo
Introduction à la validation d'un formulaire JSF
Petit historique
Rappels sur la validation d'un formulaire en HTML 5
Rendre la saisie d'un champ obligatoire avec JSF
Utilisation des tags de validation JSF
Exemple d'utilisation d'un tag de validation
Exemple d'utilisation d'un tag de validation à base d'expression régulière
Mise en oeuvre de validateurs personnalisés
Couper la validation du formulaire pour un bouton particulier
Activation du support HTML5 de validation des champs du formulaire
Utilisation de la taglib http://xmlns.jcp.org/jsf/passthrough
Utilisation de la taglib http://xmlns.jcp.org/jsf
Technique de hacking pour débrayer la validation côté client
Cette vidéo vous montre comment mettre en oeuvre la validation de vos formulaires JSF en utilisant les tags et les attributs de validation proposés par le framework.
La validation d'un formulaire HTML, n'est pas si simple qu'il n'y parait. Effectivement, dans ses premières versions le langage HTML ne proposait aucun mécanisme de validation de formulaire. Pour autant, on pouvait ajouter du code JavaScript pour vérifier le contenu de chaque champ avec la soumission du formulaire. Le problème à utiliser une validation côté client réside dans le fait qu'on peut très facilement demander les outils de développement de votre navigateur (en appuyant sur la touche F12) et débrayer ces vérifications avant la soumission de votre formulaire. Il est donc impératif de vérifier les données soumises, côté serveur.
A l'époque d'HTML 4.01, JSF a donc proposé un ensemble de possibilités afin de valider, côté serveur, les données soumises par le navigateur. Il est beaucoup plus difficile pour un hacker de tenter de débrayer une validation côté serveur. Il est donc impératif de toujours garantir qu'on vérifie les données côté serveur. Par contre, JSF ne produisait pas de code JavaScript pour compléter le processus de validation avec une couche côté client : dommage, cela aurait pu éviter des allers/retours inutiles vers le serveur tant que le formulaire n'est pas correctement renseigné.
Depuis 2014, HTML 5.0 a proposé des extensions, sur les tags HTML liés aux formulaires, permettant de simplifier considérablement la gestion de la validation côté client. Ces nouveautés HTML sont assez différentes, dans leurs mises en oeuvre, des choix fait sur JSF. Il n'est donc pas si simple de cumuler l'approche JSF avec l'approche HTML5. Il est important de noter que, comme l'approche utilisant JavaScript, il est extrêmement simple de désactiver les vérifications faites par le navigateur : les hackers peuvent se servir de ce point pour envoyer des données corrompues au serveur.
De mon point de vue, les deux approches sont complémentaires et intéressantes. Valider le formulaire côté client permet de limiter les allers/retours entre le navigateur et le serveur : tant que les données ne sont pas correctes, on réduit donc la charge du serveur et la consommation de la bande passante. Si l'utilisateur a tenté de tricher sur les données soumises au serveur, la validation côté serveur permet de sécuriser efficacement le formulaire.
HTML5 a donc enrichie les tags de formulaires avec des nouveaux attributs (ou valeurs d'attributs) permettant de renforcer le contrôle des données
saisies. En autre, HTML5 propose des nouveaux types de champs <input>
:
<input type="number" />
: permet de limiter la saisie à de valeurs numériques.
<input type="date" />
: permet de limiter la saisie à des dates.
<input type="email" />
: permet de limiter la saisie à des emails.
...
Citons aussi quelques nouveaux attributs pouvant être utilisés sur vos champs <input>
:
required
: permet d'imposer une saisie sur un tag de saisie.
pattern="regexp"
: permet de fournir une expression régulière (au format PCRE) à utiliser pour la validation du champ.
...
Le souci que je vois à utiliser ces possibilités, c'est que le rendu visuel des messages d'erreur est spécifique à chaque navigateur et surtout, ce rendu visuel est très différent de celui produit par la validation JSF. Plus tard dans ce document, je vous montrerai comment coupler la validation côté serveur avec une validation HTML5 côté client.
La première possibilité de validation, via l'API JSF, que je souhaite vous montrer consiste à rendre la saisie un champ du formulaire obligatoire.
Cela passe par l'ajoute d'un attribut required
sur vos tags JSF de saisie de valeurs. Voici un exemple de code.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<h:form> <h:messages styleClass="errorBlock" /><br/> Login: <h:inputText id="login" value="#{loginBean.login}" required="true" /> <br/> Password: <h:inputSecret id="password" value="#{loginBean.password}" required="true" /> <br/><br/> <h:commandButton action="#{loginBean.returnAction}" value="Connect" /> </h:form> |
required
agit uniquement côté serveur.
Il ne produit pas l'attribut required
HTML5 sur vos tags <input />
.
Vous aurez noté la présence d'un tag <h:messages />
: il permet de concentrer les erreurs constatées en tête du formulaire.
Il est possible d'appliquer une classe de style à ce composant (pour ma part, texte affiché en rouge).
Il est aussi possible de contrôler comment vos erreurs seront restituées.
Par défaut, c'est une liste à puces qui est produite, comme le montre la capture d'écran suivante.
Si vous souhaitez plutôt afficher vos messages d'erreur dans un tableau, fixez la valeur de l'attribut layout
à table
(par défaut, cet attribut est fixé à la valeur list
).
1 |
<h:messages styleClass="errorBlock" layout="table" /> |
Il se peut que le message d'erreur produit ne pas convienne pas (personnellement, je pense que le nom technique du tag JSF n'a rien à faire dans le message
d'erreur): dans ce cas, vous pouvez le modifier en rajoutant un attribut requiredMessage
. Voici un petit exemple d'utilisation
(ligne 6 et 11).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<h:form> <h:messages styleClass="errorBlock" /><br/> Login: <h:inputText id="login" value="#{loginBean.login}" required="true" requiredMessage="La saisie du login est obligatoire !" /> <br/> Password: <h:inputSecret id="password" value="#{loginBean.password}" required="true" requiredMessage="La saisie du mot de passe est obligatoire !" /> <br/><br/> <h:commandButton action="#{loginBean.returnAction}" value="Connect" /> </h:form> |
Voici le résultat produit en l'absence de texte saisie dans les champs du formulaire.
Je vous préfèrez avoir chaque message d'erreur à côté du champ de saisie qu'il lui est associé, il est possible d'utiliser des tags
<h:message />
(sans s
final) à la place du tag <h:messages />
. Dans ce cas, chaque tag
<h:message />
doit avoir un attribut for
pour l'associer au champ de saisie correspondant.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<h: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 !" /> <h:message for="password" styleClass="errorBlock" /> <br/><br/> <h:commandButton action="#{loginBean.returnAction}" value="Connect" /> </h:form> |
Et voici le résultat obtenu si le formulaire n'est pas correctement renseigné.
Outre le fait de rendre un champ de saisie obligatoire, il existe divers tags JSF de validation des données renseignées. Voici une liste des principaux tags de validation JSF.
<validateLongRange minimum="7" maximum="77" for="ageTintin" />
: permet de valider qu'une valeur numérique entière est bien
comprise entre deux bornes que l'on spécifie via les attributs minimum
et maximun
.
<validateDoubleRange minimum="0.0" maximum="150.99" for="price" />
: permet de valider qu'une valeur numérique flottante est
bien comprise entre deux bornes que l'on spécifie via les attributs minimum
et maximun
.
<validateLength minimum="3" maximum="12" for="txtPassword" />
: permet de valider qu'une chaîne de caractères à bien sa
longueur comprise entre deux bornes que l'on spécifie via les attributs minimum
et maximun
.
<validateRegex for="login" />
: permet de vérifier qu'une chaîne de caractères respecte bien un format donné via une
expression régulière PCRE.
L'attribut for
n'est pas obligatoire : si le tag de validation est placé dans le tag du champ de saisie,
alors l'attachement est implicitement réalisé. Dans ce cas, n'oubliez pas de correctement terminer le tag correspondant au champ de saisie.
Vous pouvez contrôler le message d'erreur produit lors de la validation grâce à l'attribut validatorMessage
de votre composant de
saisie de texte.
Commençons par un exemple de validation d'un mot de passe dont la longueur devra être comprise entre 3 à 12 caractères.
Dans cet exemple, j'ai choisi de placer le tag de validation directement comme enfant du tag <h:inputSecret />
.
1 2 3 4 5 6 7 |
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"> <f:validateLength minimum="3" maximum="12" /> </h:inputSecret> <h:message for="password" styleClass="errorBlock" /> |
Voici un autre exemple d'utilisation d'un tag de validation : nous allons vérifier que le login saisi correspond bien au format d'une adresse email. Un tag de validation basé sur une expression régulière devrait faire l'affaire.
1 2 3 4 5 6 7 8 |
Login: <h:inputText id="login" value="#{loginBean.login}" required="true" requiredMessage="La saisie du login est obligatoire !" validatorMessage="Le login doit correspondre à un email"> <f:validateRegex pattern="^[\w.+-]+@[\w.+-]+\.[a-z]{2,}$" /> </h:inputText> <h:message for="login" styleClass="errorBlock" /> <br/> |
Pour de plus amples informations sur la syntaxe des expressions régulières, je vous renvoie vers les chapitres relatifs à la mise en oeuvre d'expressions régulières en Java.
Voici les messages produits si vous ne renseignez pas correctement notre formulaire.
Il est possible de définir votre propre méthode de validation (du code Java) : cela se fait directement sur la classe de « backing bean »
utilisée par votre formulaire. Une telle méthode doit respecter une signature bien précise imposée par le framework JSF. Dans l'exemple que je vous
propose, on cherche à savoir si la donnée saisie contient bien un caractère #
.
1 2 3 4 5 6 7 8 |
public void validateAField(FacesContext context, UIComponent component, Object value) throws ValidatorException { String inputValue = (String) value; if ( ! inputValue.contains( "#" ) ) { FacesMessage msg = new FacesMessage( "Mauvais format : doit contenir un #" ); msg.setSeverity( FacesMessage.SEVERITY_ERROR ); throw new ValidatorException(msg); } } |
Et voici maintenant le code XML permettant de liée la validation de votre zone de saisie à la méthode de validation.
1 2 3 4 5 |
Truc : <h:inputText id="aField" requiredMessage="La saisie du champ est obligatoire !" validator="#{backingBean.validateAField}" /> <h:message for="aField" styleClass="errorBlock" /> <br/> |
Imaginons que vous souhaitez ajouter un bouton (avec un traitement côté serveur) qui requière de ne pas exécuter la validation : par exemple un
bouton « cancel ». Dans ce cas, il faudra lui rajouter un attribut supplémentaire pour couper la validation en cas de clic sur cet élément.
Cet attribut se nomme immediate
et peut valoir true
ou false
. Voici un exemple d'utilisation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<h: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 !" /> <h:message for="password" styleClass="errorBlock" /> <br/><br/> <h:commandButton action="#{loginBean.returnAction}" value="Connect" /> <br/> <h:commandButton action="#{loginBean.cancelAction}" value="Cancel" immediate="true" /> </h:form> |
cancelAction
(une simple méthode) sur votre classe de
« backing bean ».
Deux techniques peuvent permettre de coupler le support HTML5 de validation de formulaire avec JSF. Dans les deux cas, cela passe par l'utilisation d'une taglib (librairie de tags) particulière.
http://xmlns.jcp.org/jsf/passthrough
Si vous le souhaitez, le framework JSF propose une extension permettant de contrôler la production d'attributs HTML5 sur vos champs de saisie.
Pour profiter de cette extension, il faut ajouter une taglib supplémentaire à votre page JSF : http://xmlns.jcp.org/jsf/passthrough
.
Je vous propose d'associer cette taglib au préfixe p
(voir ligne 4).
L'exemple suivant vous montre la mise en oeuvre de cette possibilité.
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 |
<!DOCTYPE html> <html xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:p="http://xmlns.jcp.org/jsf/passthrough"> <f:view> <head> <title>Login screen</title> <link rel="stylesheet" type="text/css" href="../styles.css" /> </head> <body> <h1>Login screen</h1> <h:form> Login: <h:inputText id="login" value="#{loginBean.login}" required="true" p: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" p:required="true" requiredMessage="La saisie du mot de passe est obligatoire !" /> <h:message for="password" styleClass="errorBlock" /> <br/><br/> <h:commandButton action="#{loginBean.returnAction}" value="Connect" /> </h:form> </body> </f:view> </html> |
p:placeholder="requis"
pour afficher un message dans un champ de saisi si celui n'est pas encore renseigné.
http://xmlns.jcp.org/jsf
La seconde technique consiste à transformer un simple tag HTML en un élément JSF assujetti au cycle de vie (côté serveur) d'un composant JSF.
Cela se fait en changeant la taglib http://xmlns.jcp.org/jsf
, traditionnellement associée au préfixe jsf
.
Ainsi, vous pourrez associer les deux aspects (client et serveur) de validation du champ considéré.
Voici un petit exemple d'utilisation.
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 |
<!DOCTYPE html> <html xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:jsf="http://xmlns.jcp.org/jsf"> <f:view> <head> <title>Login screen</title> <link rel="stylesheet" type="text/css" href="../styles.css" /> </head> <body> <h1>Login screen</h1> <h:form> Login: <input jsf:id="login" jsf:value="#{loginBean.login}" type="text" jsf:required="true" required="required" jsf:requiredMessage="La saisie du login est obligatoire !" /> <h:message for="login" styleClass="errorBlock" /> <br/> Password: <input jsf:id="password" jsf:value="#{loginBean.password}" type="password" jsf:required="true" required="required" jsf:requiredMessage="La saisie du mot de passe est obligatoire !" /> <h:message for="password" styleClass="errorBlock" /> <br/><br/> <input type="submit" jsf:action="#{loginBean.returnAction}" value="Connect" /> </h:form> </body> </f:view> </html> |
required
sans lui fixer valeur ou bien en lui donnant
la valeur required
(required="required"
). La technologie Facelet, utilisée par JSF, étant basée sur du XML, la première
proposition ne peut pas fonctionner. Vous êtes donc obligé d'y définir cet attribut ainsi : required="required"
.
input
de votre formulaire. Même si par défaut, en HTML et en l'absence de l'attribut type
, un champ de saisie est considéré de type text,
il est obligatoire de le préciser pour JSF (attribute type="text"
en ligne 15 de l'exemple précédent).
Dans le cas contraire, le champ sera purement et simplement ignoré par JSF.
Si vous souhaitez tester une tentative de triche au niveau du code HTML, lancer la page Web dans votre navigateur puis appuyez sur la touche
F12
. Les outils de développement devraient apparaître comme le montre la capture d'écran ci-dessous.
Une fois les outils de développement du navigateur affichés, ouvrez bien l'onglet « Elements » puis veuillez suivre les étapes suivantes :
Cliquez une fois sur le bouton « Select an element in the page and inspect it ». Point 1, dans la capture d'écran.
Puis sélectionnez le champ <input />
sur lequel vous souhaiter travailler. Pour complétement supprimer la validation sur le
formulaire, il faudra répéter la procédure pour le login et le mot de passe.
Dans la partie inférieure, localisez l'attribut required
et double-cliquez dessus pour entrer en mode édition. Supprimer totalement
l'attribut et sa valeur.
Fermer les outils de développement et soumettre le formulaire. Si tout se passe bien, notre validation côté serveur doit rattraper la situation.
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 :