Introduction à STRUTS2 par l'exemple


précédentsommairesuivant

XIV. Exemple 11 - Conversion et validation de dates

La nouvelle application présente la saisie de dates :

Image non disponible
  • en [1], le formulaire de saisie
  • en [2], la réponse renvoyée

L'application a un fonctionnement similaire à celui de la saisie des nombres entiers aussi ne commenterons-nous que les points qui diffèrent.

XIV-A. Le projet Netbeans

Le projet Netbeans est le suivant :

Image non disponible

  • en [1], les vues de l'application
    • [Accueil.jsp] : la page d'accueil
    • [FormDate.jsp] : le formulaire de saisie
    • [ConfirmationFormDate.jsp] : la page de confirmation
  • en [2], le fichier des messages [messages.properties] et le fichier de configuration principal de Struts
  • en [3] :
    • [FormDate.java] : l'action qui affiche et traite le formulaire
    • [FormDate-validation.xml] : les règles de validation de l'action [FormDate]. Ce fichier délègue ces validations au modèle.
    • [FormDateModel] : le modèle de l'action [FormDate]
    • [ FormDateModel-validation.xml] : les règles de validation du modèle
    • [FormDateModel.properties] : le fichier des messages du modèle
    • [example.xml] : fichier de configuration secondaire de Struts

XIV-B. La configuration du projet

Le projet est principalement configuré par le fichier [example.xml] suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <package name="example" namespace="/example" extends="struts-default">
    <action name="Accueil">
      <result name="success">/example/Accueil.jsp</result>
    </action>
    <action name="FormDate" class="example.FormDate">
      <result name="input">/example/FormDate.jsp</result>
      <result name="cancel" type="redirect">/example/Accueil.jsp</result>
      <result name="success">/example/ConfirmationFormDate.jsp</result>
    </action>
  </package>
</struts>

Il est analogue à celui qui a été étudié pour la saisie des nombres entiers.

XIV-C. Les fichiers des messages

Le fichier [messages.properties] est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
Accueil.titre=Accueil
Accueil.message=Struts 2 - Conversions et validations
Accueil.FormDate=Saisie de dates
Form.titre=Conversions et validations
FormDate.message=Struts 2 - Conversion et validation de dates
FormDate.conseil=Tapez les dates au format JJ/MM/AAAA comme dans 12/10/2008
Form.submitText=Valider
Form.cancelText=Annuler
Form.clearModel=Raz mod\u00e8le
Confirmation.titre=Confirmation
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.lien=Formulaire de test
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".

Le fichier [FormDateModel.properties] est le suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
date.format={0,date,dd/MM/yyyy}
date1.prompt=1-Tapez une date au format JJ/MM/AAAA
date1.error=Date erron\u00E9e
date2.prompt=2-Tapez une date au format JJ/MM/AAAA
date2.error=Date erron\u00E9e
date3.prompt=3-Tapez une date >=18/05/2000
date3.error=Date erron\u00E9e
date4.prompt=4-Tapez une date <=20/06/2001
date4.error=Date erron\u00E9e
date5.prompt=5-Tapez une date dans l''intervalle [18/05/2000,20/06/2001]
date5.error=Date errron\u00E9e
date6.prompt=6-Tapez une date dans l''intervalle [18/05/2000,20/06/2001]
date6.error=Date errron\u00E9e

La ligne 1 joue un rôle important. Nous y reviendrons.

XIV-D. Le formulaire de saisie

La vue [FormDate.jsp] est la suivante :

 
Sélectionnez
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.
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Form.titre"/></title>
    <s:head/>
  </head>

  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="FormDate.message"/></h2>
    <h4><s:text name="FormDate.conseil"/></h4>
    <s:form name="formulaire" action="FormDate">
      <s:textfield name="date1" key="date1.prompt"/>
      <s:textfield name="date2" key="date2.prompt" value="%{#parameters['date2']!=null ? #parameters['date2'] : date2==null ? '' :getText('date.format',{date2})}"/>
      <s:textfield name="date3" key="date3.prompt" value="%{#parameters['date3']!=null ? #parameters['date3'] : date3==null ? '' :getText('date.format',{date3})}"/>
      <s:textfield name="date4" key="date4.prompt" value="%{#parameters['date4']!=null ? #parameters['date4'] : date4==null ? '' :getText('date.format',{date4})}"/>
      <s:textfield name="date5" key="date5.prompt" value="%{#parameters['date5']!=null ? #parameters['date5'] : date5==null ? '' :getText('date.format',{date5})}"/>
      <s:textfield name="date6" key="date6.prompt"/>
      <s:submit key="Form.submitText" method="execute"/>
    </s:form>
    <br/>
    <s:url id="url" action="FormDate" method="cancel"/>
    <s:a href="%{url}"><s:text name="Form.cancelText"/></s:a>
      <br/>
    <s:url id="url" action="FormDate" method="clearModel"/>
    <s:a href="%{url}"><s:text name="Form.clearModel"/></s:a>
  </body>
</html>

Les lignes 13 à 18 sont les six zones de saisie de dates. Les champs date2 à date5 ont un attribut value complexe, le même que pour la saisie des nombres réels. Les mêmes difficultés ayant été rencontrées, on les a résolues de la même façon.

XIV-E. La vue de confirmation

La vue de confirmation [ConfirmationFormDate.jsp] est la suivante :

Image non disponible

Son code est le suivant :

 
Sélectionnez
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Confirmation.titre"/></title>
    <s:head/>
  </head>

  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="Confirmation.message"/></h2>
    <table border="1">
      <tr>
        <th><s:text name="Confirmation.champ"/></th>
        <th><s:text name="Confirmation.valeur"/></th>
      </tr>
      <tr>
        <td><s:text name="date1.prompt"/></td>
        <td><s:text name="date1"/></td>
      </tr>
      <tr>
        <td><s:text name="date2.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date2"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date3.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date3"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date4.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date4"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date5.prompt"/></td>
        <td>
          <s:text name="date.format">
            <s:param value="date5"/>
          </s:text>
        </td>
      </tr>
      <tr>
        <td><s:text name="date6.prompt"/></td>
        <td><s:text name="date6"/></td>
      </tr>
    </table>
    <br/>
    <s:url id="url" action="FormDate!input"/>
    <s:a href="%{url}"><s:text name="Confirmation.lien"/></s:a>
  </body>
</html>

La vue [ConfirmationFormDate.jsp] se contente d'afficher le modèle [FormDateModel]. Les lignes 47-49 montrent l'affichage d'une date. On veut que cet affichage tienne compte d'un format de date. Pour cela, on utilise la balise <s:text> qu'on avait utilisée jusque là pour l'internationalisation de l'application.

Ici, la clé de message utilisée est date.format. Cette clé est trouvée dans le fichier [FormDateModel.properties] :

 
Sélectionnez
date.format={0,date,dd/MM/yyyy}

La valeur associée à la clé n'est pas ici un message, mais un format d'affichage :

  • 0 est un paramètre représentant la valeur à afficher. Ici ce sera le champ date5 du modèle.
  • date représente une date. Précédemment, on avait number pour un nombre.
  • dd/MM/yyyy représente le format de la date. Ici, on le veut sous la forme jj/mm/aaaa. Le format Java qui convient est dd/MM/yyyy (d : day, M : Month, y : year)

La balise

 
Sélectionnez
1.
2.
3.
<s:text name="date.format">
            <s:param value="date5"/>
</s:text>

Fait afficher le message {0, date, dd/MM/yyyy} où le paramètre date5 (ligne 2) viendra remplacer le paramètre 0 du format. Ainsi, le modèle date5 sera affiché au format jj/mm/aaaa.

XIV-F. Le modèle [FormDateModel]

Les champs date1 à date6 du formulaire [FormDate.jsp] sont injectés dans le modèle [FormDateModel] suivant :

 
Sélectionnez
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.
package example;
import java.util.Date;

public class FormDateModel {

  // constructeur sans paramètre
  public FormDateModel() {
  }
  // champs
  private String date1;
  private Date date2 = new Date();
  private Date date3 = new Date();
  private Date date4;
  private Date date5;
  private String date6;

  // raz modèle
  public void clearModel() {
    date1 = null;
    date2 = null;
    date3 = null;
    date4 = null;
    date5 = null;
    date6 = null;
  }

  // getters et setters
...
}
  • les champs date1 et date6 sont de type String
  • les autres champs sont de type Date

XIV-G. La validation du modèle

La validation du modèle est contrôlée par deux fichiers : [FormDate-validation.xml] et [FormDateModel-validation.xml].

Le fichier [FormDate-validation.xml] délègue les validations à [FormDateModel-validation.xml] :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/exemple-10/example/xwork-validator-1.0.2.dtd">

<validators>
  <field name="model" >
    <field-validator type="visitor">
      <param name="appendPrefix">false</param>
      <message/>
    </field-validator>
  </field>
</validators>

Nous avons déjà rencontré ce fichier.

Le fichier [FormDateModel-validation.xml] contient les règles de validation suivantes :

 
Sélectionnez
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/exemple-11/example/xwork-validator-1.0.2.dtd">

<validators>

  <field name="date1" >
    <field-validator type="requiredstring" short-circuit="true">
      <message key="date1.error"/>
    </field-validator>
    <field-validator type="regex" short-circuit="true">
      <param name="expression">^\d{2}/\d{2}/\d{4}$</param>
      <param name="trim">true</param>
      <message key="date1.error"/>
    </field-validator>
  </field>

  <field name="date2" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
   </field>

  <field name="date3" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date3.error"/>
    </field-validator>
    <field-validator type="date" short-circuit="true">
      <param name="min">18/05/2000</param>
      <message key="date3.error"/>
    </field-validator>
  </field>

  <field name="date4" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date4.error"/>
    </field-validator>
    <field-validator type="date" short-circuit="true">
      <param name="max">20/06/2001</param>
      <message key="date4.error"/>
    </field-validator>
  </field>

 <field name="date5" >
    <field-validator type="required" short-circuit="true">
      <message key="date2.error"/>
    </field-validator>
    <field-validator type="conversion" short-circuit="true">
      <message key="date5.error"/>
    </field-validator>
    <field-validator type="date" short-circuit="true">
      <param name="min">18/05/2000</param>
      <param name="max">20/06/2001</param>
      <message key="date5.error"/>
    </field-validator>
  </field>

  <field name="date6" >
    <field-validator type="requiredstring" short-circuit="true">
      <message key="date6.error"/>
    </field-validator>
    <field-validator type="regex" short-circuit="true">
      <param name="expression">^\d{2}/\d{2}/\d{4}$</param>
      <param name="trim">true</param>
      <message key="date6.error"/>
    </field-validator>
  </field>

</validators>
  • la règle des lignes 11-20 vérifie que le champ de saisie date1 suit le modèle d'une expression régulière représentant une date de la forme jj/mm/aaaa. On notera qu'on vérifie que la chaîne a la forme d'une date mais qu'on ne vérifie pas que c'est une date valide. Ainsi 29/02/2011 a la forme d'une date mais n'est pas une date valide.
  • la règle des lignes 22-29 vérifie que le champ de saisie date2 est une date valide.
  • la règle des lignes 31-42 vérifie que le champ de saisie date3 est une date valide >=18/05/2000
  • la règle des lignes 44-55 vérifie que le champ de saisie date4 est une date valide <=20/06/2001.
  • la règle des lignes 57-69 vérifie que le champ de saisie date5 est une date valide <=20/06/2001 et >=18/05/2000.
  • la règle des lignes 71-80 vérifie que le champ date6 a la forme d'une date jj/mm/aaaa.

Une fois le fichier [FormDateModel-validation.xml] exploité par l'intercepteur de validation, celui-ci fait exécuter la méthode validate de l'action [FormDate] si elle existe. Nous allons la présenter en même temps que la totalité de l'action.

XIV-H. L'action [FormDate]

L'action [FormDate] est la suivante :

 
Sélectionnez
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
package example;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;

public class FormDate extends ActionSupport implements ModelDriven, SessionAware {

  // constructeur sans paramètre
  public FormDate() {
  }

  // modèle de l'action
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormDateModel());
    }
    return session.get("model");
  }

  @SkipValidation
  public String clearModel() {
    // raz du modèle
    ((FormDateModel) getModel()).clearModel();
    // résultat
    return INPUT;
  }

  public String cancel() {
    // on nettoie le modèle
    ((FormDateModel) getModel()).clearModel();
    // résultat
    return "cancel";
  }
  // SessionAware
  Map<String, Object> session;

  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  // validation
  @Override
  public void validate() {
    // formatage des dates
    SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
    formateurDate.setLenient(false);
    // saisie date1 valide ?
    if (getFieldErrors().get("date1") == null) {
      // vérification validité date
      try {
        formateurDate.parse(((FormDateModel) getModel()).getDate1());
      } catch (Exception e) {
        addFieldError("date1", getText("date1.error"));
      }
    }
    // saisie date6 valide ?
    if (getFieldErrors().get("date6") == null) {
      Date d = null;
      try {
        // vérification validité date
        d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
        // vérification bornes
        if (d.after(formateurDate.parse("20/06/2001")) || d.before(formateurDate.parse("18/05/2000"))) {
          addFieldError("date6", getText("date6.error"));
          return;
        }
      } catch (Exception e) {
        addFieldError("date6", getText("date6.error"));
        return;
      }
    }
  }
}

L'action [FormDate] est bâtie sur le même modèle que l'action [FormInt]. Nous ne commenterons que la méthode validate. On rappelle que la méthode validate est exécutée après exploitation du fichier de validation [FormDateModel-validation.xml] et avant l'exécution de la méthode execute.

  • la méthode validate complète la validation des dates date1 et date6. Le fichier de validation [FormDateModel-validation.xml] avait vérifié que les chaînes saisies avaient la forme d'une date. On vérifie maintenant que ce sont bien des dates valides. On rappelle également que date1 et date6 étaient les deux seuls champs du modèle à être de type String.
  • ligne 50 : on crée un format de date jj/mm/aaaa afin de vérifier que les chaînes de ce type sont bien des dates valides.
  • ligne 51 : setLenient(false) permet d'éviter que la date invalide 32/01/2012 soit interprétée comme la date valide 01/02/2012.
  • ligne 53 : on ne valide date1 que si les précédentes validations n'ont pas eu d'erreurs sur ce champ.
  • ligne 56 : on vérifie que date1 est une date valide au format jj/mm/aaaa.
  • ligne 58 : si ce n'est pas le cas, on associe un message d'erreur au champ date1.
  • ligne 62 : on ne valide date6 que si ce champ a passé les précédentes validations.
  • ligne 66 : on vérifie que date6 est une date valide
  • ligne 68 : si date6 est <18/05/2000 ou date6>20/06/2001 alors date6 est incorrect.

XIV-I. Derniers détails

Le formulaire précédent présente des failles comme le montre l'exemple suivant :

Image non disponible
  • en [1], on rentre une date invalide
  • en [2], elle a été acceptée. Seuls les premiers caractères jj/mm/aaaa ont été pris en compte.

Les champs date2 à date5 qui sont liés à des modèles de type Date ont tous ce problème. Là encore, peut-être ai-je raté quelque chose dans la documentation. Les champs date1 et date6 qui sont liés à des modèles de type String n'ont pas ce problème.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2012 Serge Tahe. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.