Skip to content

5. Formularze dynamiczne z ograniczeniami integralności

Teraz stworzymy nową aplikację o nazwie strutspersonne2, wykorzystującą

  • dynamicznego formularza, podobnie jak w strutspersonne1
  • pliku deklaracji ograniczeń integralności, które mają być sprawdzane przez pola tego dynamicznego formularza

5.1. Deklaracja ograniczeń integralności

Klasa służąca do przechowywania wartości „nazwisko” i „wiek” w aplikacji strutspersonne1 została zadeklarowana w następujący sposób:

    <form-beans>
        <form-bean name="frmPersonne" type="istia.st.struts.personne.PersonneDynaForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
    </form-beans>

Musieliśmy utworzyć klasę PersonneDynaForm, aby dysponować metodą validate zdolną do sprawdzenia, czy wartości pól „nazwisko” i „wiek” w dynamicznym formularzu są prawidłowe. Należało przeprowadzić dwa rodzaje weryfikacji:

  • oba pola nie mogły być puste
  • pole „age” musiało spełniać wymogi maski (wyrażenia regularnego) \s*\d+\s*

Te dwie weryfikacje należą do sprawdzanych przez środowisko StrutsValidator. Środowisko to jest dostarczane wraz z biblioteką Struts i zawiera pewną liczbę klas, które można znaleźć w klasach commons-validator.jar oraz jakarta-oro.jar. Jeśli postępowali Państwo zgodnie z procedurą instalacji bibliotek Struts w Tomcacie, opisaną na początku tego dokumentu, biblioteki te są już dostępne w Tomcacie. Należy upewnić się, że są one również dostępne w JBuilderze. Zostało to również wyjaśnione na początku tego dokumentu.

Nowa deklaracja formularza w pliku struts-config.xml wygląda następująco:

    <form-beans>
        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
    </form-beans>

Różnica jest więc bardzo niewielka. Klasą powiązaną z formularzem dynamicznym jest teraz predefiniowana klasa z StrutsValidator: org.apache.struts.validator.DynaValidatorForm. Programista nie musi już tworzyć klasy. Określa on ograniczenia integralności, które formularz musi sprawdzać, w oddzielnym pliku XML. Kontroler Struts musi znać nazwę tego pliku. W tym celu w pliku struts-config.xml pojawia się nowa sekcja konfiguracyjna:

    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
        <set-property 
        property="pathnames" 
      value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"
    />
    </plug-in>

Sekcja <plug-in> służy do ładowania klasy zewnętrznej względem Struts. Jej głównym atrybutem jest classname, który określa nazwę klasy, której instancja ma zostać utworzona. Utworzony obiekt może wymagać zainicjowania. Odbywa się to za pomocą tagów set-property, które posiadają dwa atrybuty:

  • property: nazwa właściwości, którą należy zainicjować
  • value: wartość właściwości

W tym przypadku klasa DynaValidatorForm musi znać dwie informacje:

  • plik XML, który definiuje standardowe ograniczenia integralności, które klasa potrafi sprawdzić,
  • plik XML definiujący ograniczenia integralności różnych dynamicznych formularzy aplikacji

Te dwie informacje są tutaj podane przez właściwość pathnames. Wartością tej właściwości jest lista plików XML, które walidator załaduje:

  • validator-rules.xml to plik definiujący standardowe ograniczenia integralności. Jest on dostarczany wraz z frameworkiem Struts. Znajduje się on w katalogu <struts>\lib wraz z plikiem definicji DTD:

Image

  • validation.xml definiuje ograniczenia integralności różnych dynamicznych formularzy aplikacji. Jest tworzony przez programistę. Jego nazwa jest dowolna.

Oba te pliki można umieścić w dowolnym miejscu w katalogu WEB-INF. W naszym przykładzie zostaną one umieszczone bezpośrednio w katalogu WEB-INF:

Image

5.2. Zapisywanie ograniczeń integralności formularzy dynamicznych

Plik validation.xml będzie zawierał nasze ograniczenia integralności dla pól „nazwa” i „wiek” formularza frmPersonne typu org.apache.struts.validator.DynaValidatorForm. Jego zawartość jest następująca:

<form-validation>
    <global>
      <constant>
        <constant-name>entierpositif</constant-name>
        <constant-value>^\s*\d+\s*$</constant-value>
    </constant>
  </global>      
    <formset>
        <form name="frmPersonne">
          <field property="nom" depends="required">
             <arg0 key="personne.nom"/>      
          </field>
          <field property="age" depends="required,mask">            
             <arg0 key="personne.age"/>      
              <var>
                  <var-name>mask</var-name>
                  <var-value>${entierpositif}</var-value>
              </var>
          </field>
    </form>
    </formset>
</form-validation>    

Należy przestrzegać następujących zasad pisania:

  • wszystkie reguły znajdują się w tagu <form-validation>
  • Tag <global> służy do definiowania informacji o zasięgu globalnym, c.a.d. Obowiązują one dla wszystkich formularzy, jeśli jest ich więcej niż jeden. W tym przypadku w tagu <global> umieszczono stałe. Stała jest definiowana przez swoją nazwę (tag <constant-name>) i wartość (tag <constant-value>). Definiujemy stałą „entierpositif” o wartości wyrażenia regularnego, które musi spełniać dodatnia liczba całkowita: ^\s*\d+\s*$ (sekwencja cyfr, przed którą i/lub po której mogą występować spacje).
  • tag <formset> definiuje zbiór formularzy, dla których należy sprawdzić ograniczenia integralności
  • tag <form name="unFormulaire"> służy do definiowania ograniczeń integralności konkretnego formularza, którego nazwa jest podana w atrybucie name. Nazwa ta musi występować na liście formularzy zdefiniowanych w struts-config.xml. W tym przypadku używany formularz frmPersonne jest zdefiniowany w pliku struts-config.xml w następującej sekcji:
    <form-beans>
        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
  • Tag <form> zawiera tyle tagów <field>, ile jest ograniczeń integralności, które należy sprawdzić w formularzu. Tag <field> posiada następujące atrybuty:
    • property: nazwa pola formularza, dla którego definiuje się ograniczenia integralności
    • depends: lista reguł integralności, które należy sprawdzić.
      • Możliwe ograniczenia to: required (pole nie może być puste), mask (wartość pola musi odpowiadać wyrażeniu regularnemu zdefiniowanemu przez zmienną mask), integer: wartość pola musi być liczbą całkowitą, byte (bajt), long (liczba całkowita typu long), float (liczba zmiennoprzecinkowa typu single), double (liczba zmiennoprzecinkowa typu double), short (liczba całkowita typu short), date (wartość pola musi być prawidłową datą), range (wartość pola musi mieścić się w podanym przedziale), email: (wartość pola musi być prawidłowym adresem e-mail), ...
      • Ograniczenia integralności są sprawdzane w kolejności określonej przez atrybut „depends”. Jeśli jedno ograniczenie nie zostanie spełnione, kolejne nie są sprawdzane.
      • każde ograniczenie jest powiązane z komunikatem o błędzie zdefiniowanym przez klucz. Oto kilka z nich w postaci ograniczenie (klucz): required (errors.required), mask (errors.invalid), integer (errors.integer), byte (errors.byte), long (errors.long), ...
      • komunikaty o błędach związane z powyższymi kluczami są zdefiniowane w pliku validator-rules.xml:
   # Komunikaty o błędach Struts Validator
   errors.required={0} is required.
   errors.minlength={0} can not be less than {1} characters.
   errors.maxlength={0} can not be greater than {1} characters.
   errors.invalid={0} is invalid.

   errors.byte={0} must be a byte.
   errors.short={0} must be a short.
   errors.integer={0} must be an integer.
   errors.long={0} must be a long.
   errors.float={0} must be a float.
   errors.double={0} must be a double.

   errors.date={0} is not a date.
   errors.range={0} is not in the range {1} through {2}.
   errors.creditcard={0} is an invalid credit card number.
   errors.email={0} is an invalid e-mail address.
  • Jak widać powyżej, komunikaty są w języku angielskim. Ponadto znajdują się one w komentarzach w pliku, w którym zaznaczono, że należy je umieścić w pliku komunikatów aplikacji. Przypomnijmy, że jest on zdefiniowany w sekcji pliku struts-config.xml:
    <message-resources 
      parameter="ressources.personneressources"
    null="false"
  />    

Atrybut „parameter” wskazuje, że komunikaty aplikacji znajdują się w pliku

WEB-INF/classes/ressources/personneressources.properties.

Komunikaty o błędach należy zatem umieścić w tym pliku. Zostaną one dodane do już istniejących:

personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

   # Komunikaty o błędach Struts Validator
    # klucz jest z góry zdefiniowany i nie należy go zmieniać
    # powiązany komunikat o błędzie jest dowolny
    # komunikat może mieć maksymalnie 4 parametry od {0} do {3}

   errors.required=<li>Le champ [{0}] doit être renseigné.</li>
   errors.minlength=<li>Le champ [{0}] foit avoir au moins {1} caractère.</li>
   errors.maxlength=<li>Le champ [{0}] ne peut avoir plus de {1} caractères.</li>
   errors.invalid=<li>Le champ [{0}] est incorrect.</li>

   errors.byte=<li>{0} doit être un octet.</li>
   errors.short=<li>{0} doit être un entier court.</li>
   errors.integer=<li>{0} doit être un entier.</li>
   errors.long=<li>{0} doit être un entier long.</li>
   errors.float=<li>{0} doit être un réel simple.</li>
   errors.double=<li>{0} doit être un réel double.</li>

   errors.date=<li>{0} n'est pas une date valide.</li>
   errors.range=<li>{0} doit être dans l'intervalle {1} à {2}.</li>
   errors.creditcard=<li>{0} n'est pas un numéro de carte valide.</li>
   errors.email=<li>{0} n'est pas une adresse électronique valide.</li>

   personne.nom=nom
     personne.age=age

Omówmy po kolei ograniczenia dotyczące integralności pliku validation.xml:

    <formset>
        <form name="frmPersonne">
          <field property="nom" depends="required">
             <arg0 key="personne.nom"/>      
          </field>
...
    </form>
    </formset>

Należy pamiętać, że te ograniczenia dotyczące integralności mają zastosowanie do pól „nazwa” i „wiek” w dynamicznym formularzu zdefiniowanym w struts-config.xml:

        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            

Ważne jest, aby nazwy formularza i jego pól były identyczne w obu plikach. Ograniczenie integralności dotyczące pola „nazwa” (property="nazwa") określa, że pole to nie może być puste (depends="required"). Jeśli tak nie jest, zostanie wygenerowany obiekt ActionError o kluczu errors.required. Komunikat powiązany z tym kluczem znajduje się w pliku personne.ressources.properties:

   errors.required=<li>Le champ [{0}] doit être renseigné.</li>

Widać, że komunikat ten wykorzystuje parametr {0}. Jego wartość jest określona przez tag <arg0> w ograniczeniu integralności:

             <arg0 key="personne.nom"/>      

Również w tym przypadku arg0 jest oznaczone kluczem, który można znaleźć również w pliku komunikatów:

   personne.nom=nom

Jeśli połączymy to wszystko, komunikat o błędzie generowany w przypadku, gdy pole „nazwa” nie jest wypełnione, brzmi:

<li>Le champ [nom] doit être renseigné.</li>

Przeanalizujmy teraz drugie ograniczenie, dotyczące pola „age”:

    <global>
      <constant>
        <constant-name>entierpositif</constant-name>
        <constant-value>^\s*\d+\s*$</constant-value>
    </constant>
  </global>      
    <formset>
        <form name="frmPersonne">
...
          <field property="age" depends="required,mask">            
             <arg0 key="personne.age"/>      
              <var>
                  <var-name>mask</var-name>
                  <var-value>${entierpositif}</var-value>
              </var>
          </field>
    </form>
    </formset>

Dla pola „age” istnieją dwa ograniczenia: „required” i „mask”. Można powtórzyć poprzednie wyjaśnienie dotyczące ograniczenia „required”. Wynika z tego, że komunikat o błędzie związany z tym ograniczeniem będzie brzmiał:

<li>Le champ [age] doit être renseigné.</li>

Drugim ograniczeniem jest mask. Oznacza to, że zawartość pola musi odpowiadać wzorcowi wyrażonemu za pomocą wyrażenia regularnego. Wartość tego wyrażenia jest zdefiniowana w tagu <var>, który definiuje zmienną o nazwie mask (<var-name>), której wartość wynosi ${entierpositif} (<var-value>). entierpositif jest stałą zdefiniowaną w sekcji <global> pliku, której wartością jest wyrażenie regularne ^\s*\d+\s*$. Warunkiem integralności jest zatem to, że wiek musi być ciągiem jednej lub kilku cyfr, przed którym lub po którym mogą występować spacje. Jeśli warunek ten nie zostanie spełniony, zostanie wygenerowany obiekt o kluczu errors.invalid o nazwie ActionError. W pliku komunikatów klucz ten jest powiązany z następującym komunikatem:

   errors.invalid=<li>Le champ [{0}] est incorrect.</li>

Ograniczenie musi określać wartość dla parametru {0}. Odbywa się to za pomocą znacznika <arg0>:

             <arg0 key="personne.age"/>      

W pliku komunikatów klucz personne.age jest powiązany z następującym komunikatem:

     personne.age=age

Komunikat o błędzie, który zostanie wygenerowany, jeśli warunek mask nie zostanie spełniony, brzmi zatem:

<li>Le champ [age] est incorrect.</li>

5.3. Klasy aplikacji

W aplikacji Struts należy napisać klasy formularzy (ActionForm lub pochodne) oraz klasy akcji (Action lub pochodne). W nowej aplikacji strutspersonne2 nie ma już klasy formularza. Zawartość formularza jest zdefiniowana w klasie struts-config.xml, a powiązane ograniczenia integralności w klasach WEB-INF/validation.xml. W aplikacji strutspersonne1 klasa FormulaireAction służąca do przetwarzania formularza miała następujący wygląd:

package istia.st.struts.personne;

....

public class FormulaireAction
  extends Action {
  public ActionForward execute(ActionMapping mapping, ActionForm form,
                               HttpServletRequest request, HttpServletResponse response) throws IOException,
    ServletException {
     // mamy poprawny formularz, w przeciwnym razie nie dotarliśmy byśmy do tego miejsca
    DynaActionForm formulaire=(DynaActionForm)form;
    request.setAttribute("nom",formulaire.get("nom"));
    request.setAttribute("age",formulaire.get("age"));
    return mapping.findForward("reponse");
  }//wykonaj
}

Klasa FormulaireAction oczekiwała otrzymania formularza w postaci obiektu DynaActionForm (kod w ramce). Jednak w pliku struts-config.xml klasa formularza jest zdefiniowana w następujący sposób:

        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
...
        </form-bean>            

Formularz zostanie zatem umieszczony w obiekcie typu DynaValidatorForm. Okazuje się, że klasa ta wywodzi się z klasy DynaActionForm. Metoda execute naszej klasy FormulaireAction pozostaje zatem ważna. Nie ma potrzeby jej przepisywania.

5.4. Wdrożenie i testowanie aplikacji strutspersonne2

5.4.1. Tworzenie kontekstu

Tę nową aplikację nazwaliśmy strutspersonne2. Tworzymy nową definicję w pliku <tomcat>\conf\serveur.xml serwera Tomcat 4.x:

                <Context path="/strutspersonne2" docBase="e:/data/serge/web/struts/personne2" />

Po wykonaniu tej czynności należy ponownie uruchomić Tomcat. Prawidłowość działania kontekstu można sprawdzić, wywołując stronę URL:

http://localhost:8080/strutspersonne2/

Image

5.4.2. Widoki

Skopiujemy folder „vues” z aplikacji „strutspersonne1” do folderu aplikacji „strutspersonne2”. Widoki bowiem nie uległy zmianie.

Image

5.4.3. Folder „WEB-INF”

Skopiujemy folder „WEB-INF” z aplikacji „strutspersonne1” do folderu aplikacji „strutspersonne2”. Zmieniło się kilka plików:

Image

Plik konfiguracyjny struts-config.xml przyjmuje następującą postać:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    <form-beans>
        <form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
            <form-property name="nom" type="java.lang.String" initial=""/>
            <form-property name="age" type="java.lang.String" initial=""/>
        </form-bean>            
    </form-beans>

    <action-mappings>

      <action
          path="/main"
          name="frmPersonne"
            validate="true"
            input="/erreurs.do"
            scope="session"
          type="istia.st.struts.personne.FormulaireAction"
      >
            <forward name="reponse" path="/reponse.do"/>
        </action>

      <action
          path="/erreurs"
          parameter="/vues/erreurs.personne.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/reponse"
          parameter="/vues/reponse.personne.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

      <action
          path="/formulaire"
          parameter="/vues/formulaire.personne.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
    </action-mappings>

    <message-resources 
      parameter="ressources.personneressources"
    null="false"
  />    

    <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
        <set-property 
        property="pathnames" 
      value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"
    />
    </plug-in>

</struts-config>

Ten plik jest identyczny z plikiem aplikacji strutspersonne1, z wyjątkiem dynamicznej definicji formularza oraz dodania wtyczki walidacyjnej (części zaznaczone ramką).

Do pliku WEB-INF dodajemy następujący plik walidacji validation.xml:

<form-validation>
    <global>
      <constant>
        <constant-name>entierpositif</constant-name>
        <constant-value>^\s*\d+\s*$</constant-value>
    </constant>
  </global>      
    <formset>
        <form name="frmPersonne">
          <field property="nom" depends="required">
             <arg0 key="personne.nom"/>      
          </field>
          <field property="age" depends="required,mask">            
             <arg0 key="personne.age"/>      
              <var>
                  <var-name>mask</var-name>
                  <var-value>${entierpositif}</var-value>
              </var>
          </field>
    </form>
    </formset>
</form-validation>

Do pliku WEB-INF dodajemy pliki validator-rules.xml i validator-rules_1_1.dtd, które znajdują się w katalogu <struts>\lib:

Image

W folderze WEB-INF/classes pozostała tylko jedna klasa:

Image

W folderze WEB-INF\classes\ressources znajduje się następujący plik komunikatów personneressources.properties:

personne.formulaire.nom.vide=<li>Vous devez indiquer un nom</li>
personne.formulaire.age.vide=<li>Vous devez indiquer un age</li>
personne.formulaire.age.incorrect=<li>L'âge est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

   # Komunikaty o błędach z walidatora Struts
    # klucz jest z góry zdefiniowany i nie należy go zmieniać
    # treść powiązanego komunikatu o błędzie jest dowolna
    # komunikat może mieć maksymalnie 4 parametry od {0} do {3}

   errors.required=<li>Le champ [{0}] doit être renseigné.</li>
   errors.minlength=<li>Le champ [{0}] foit avoir au moins {1} caractère.</li>
   errors.maxlength=<li>Le champ [{0}] ne peut avoir plus de {1} caractères.</li>
   errors.invalid=<li>Le champ [{0}] est incorrect.</li>

   errors.byte=<li>{0} doit être un octet.</li>
   errors.short=<li>{0} doit être un entier court.</li>
   errors.integer=<li>{0} doit être un entier.</li>
   errors.long=<li>{0} doit être un entier long.</li>
   errors.float=<li>{0} doit être un réel simple.</li>
   errors.double=<li>{0} doit être un réel double.</li>

   errors.date=<li>{0} n'est pas une date valide.</li>
   errors.range=<li>{0} doit être dans l'intervalle {1} à {2}.</li>
   errors.creditcard=<li>{0} n'est pas un numéro de carte valide.</li>
   errors.email=<li>{0} n'est pas une adresse électronique valide.</li>

   personne.nom=nom
  personne.age=age

Image

5.5. Testy

Jesteśmy gotowi do przeprowadzenia testów. Poniżej zamieszczamy kilka zrzutów ekranu, które czytelnik powinien spróbować odtworzyć.

Wymagane są: URLhttp://localhost:8080/strutspersonne2/formulaire.do:

Image

Należy użyć przycisku [Envoyer] bez wypełniania pól:

Image

Próbujemy ponownie, ale pojawia się błąd w polu „age”:

Image

Otrzymujemy następującą odpowiedź:

Image

Próbujemy ponownie, tym razem podając prawidłowe wartości:

Image

Otrzymujemy następującą odpowiedź:

Image

5.6. Wniosek

Wykazaliśmy, że stosowanie formularzy dynamicznych, w których ograniczenia integralności są „standardowe”, pozwala uniknąć tworzenia klas służących do ich reprezentowania. Jeśli ograniczenia integralności wykraczają poza standard, konieczne staje się ponowne tworzenie klas w celu weryfikacji tych nowych ograniczeń.