5. Dynamische formulieren met integriteitsbeperkingen
We gaan nu een nieuwe applicatie ontwikkelen met de naam strutspersonne2, waarbij gebruik wordt gemaakt van
- een dynamisch formulier zoals in strutspersonne1
- een bestand met integriteitsbeperkingen die door de velden van dit dynamische formulier moeten worden gecontroleerd
5.1. Definitie van integriteitsbeperkingen
De klasse die werd gebruikt om de waarden voor naam en leeftijd van de applicatie strutspersonne1 op te slaan, was als volgt gedefinieerd:
<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>
We moesten de klasse PersonneDynaForm schrijven om te beschikken over een validatiemethode die kon controleren of de waarden voor ‘naam’ en ‘leeftijd’ in het dynamische formulier geldig waren. Er moesten twee soorten controles worden uitgevoerd:
- beide velden mochten niet leeg zijn
- het veld ‘age’ moest voldoen aan het masker (reguliere expressie) \s*\d+\s*
Deze twee controles behoren tot de controles die de omgeving StrutsValidator kan uitvoeren. Deze omgeving wordt meegeleverd met Struts en bevat een aantal klassen die te vinden zijn in commons-validator.jar en jakarta-oro.jar. Als u de procedure hebt gevolgd die aan het begin van dit document is uitgelegd voor het installeren van de Struts-bibliotheken in Tomcat, zijn deze bibliotheken al beschikbaar in Tomcat. Zorg ervoor dat ze ook beschikbaar zijn in JBuilder. Dit is eveneens aan het begin van dit document uitgelegd.
De nieuwe declaratie van het formulier in struts-config.xml ziet er als volgt uit:
<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>
Er is dus nauwelijks verschil. De klasse die aan het dynamische formulier is gekoppeld, is nu een vooraf gedefinieerde klasse van StrutsValidator: org.apache.struts.validator.DynaValidatorForm. De ontwikkelaar hoeft geen klasse meer te schrijven. Hij specificeert de integriteitsbeperkingen die het formulier moet controleren in een apart bestand XML. De Struts-controller moet de naam van dit bestand kennen. Hiervoor verschijnt een nieuwe configuratiesectie in het bestand struts-config.xml:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"
/>
</plug-in>
De sectie <plug-in> wordt gebruikt om een klasse te laden die extern is aan Struts. Het belangrijkste attribuut is classname, dat de naam aangeeft van de klasse die moet worden geïnstantieerd. Het geïnstantieerde object moet mogelijk worden geïnitialiseerd. Dit gebeurt met behulp van set-property-tags, die twee attributen hebben:
- property: de naam van de eigenschap die moet worden geïnitialiseerd
- value: de waarde van de eigenschap
In dit geval moet de klasse DynaValidatorForm twee gegevens kennen:
- het bestand XML, dat de standaardintegriteitsbeperkingen definieert die de klasse kan controleren.
- het bestand XML dat de integriteitsbeperkingen van de verschillende dynamische formulieren van de applicatie definieert
Deze twee gegevens worden hier opgegeven via de eigenschap **pathnames**. De waarde van deze eigenschap is een lijst met XML-bestanden die de validator zal laden:
- validator-rules.xml is het bestand waarin de standaardintegriteitsvoorwaarden zijn gedefinieerd. Dit wordt meegeleverd met Struts. Het is te vinden in <struts>\lib, samen met het bijbehorende definitiebestand DTD:
![]()
- validation.xml definieert de integriteitsbeperkingen van de verschillende dynamische formulieren van de applicatie. Dit bestand wordt door de ontwikkelaar aangemaakt. De naam ervan is vrij te kiezen.
Deze twee bestanden kunnen overal onder WEB-INF worden geplaatst. In ons voorbeeld worden ze direct onder WEB-INF geplaatst:

5.2. Integriteitsregels voor dynamische formulieren vastleggen
Het bestand validation.xml bevat onze integriteitsregels voor de velden ‘naam’ en ‘leeftijd’ van het formulier frmPersonne van het type org.apache.struts.validator.DynaValidatorForm. De inhoud ervan is als volgt:
<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>
De volgende schrijfregels moeten worden nageleefd:
- alle regels staan in een <form-validation>-tag
- De tag <global> wordt gebruikt om informatie met een globaal bereik te definiëren, c.a.d. Dit geldt voor alle formulieren, mochten er meerdere zijn. Hier hebben we constanten in <global> geplaatst. Een constante wordt gedefinieerd door zijn naam (tag <constant-name>) en zijn waarde (tag <constant-value>). We definiëren de constante `entierpositief` met als waarde de reguliere expressie waaraan een positief geheel getal moet voldoen: ^\s*\d+\s*$ (een reeks cijfers, eventueel voorafgegaan en/of gevolgd door spaties).
- De tag <formset> definieert de verzameling formulieren waarvoor integriteitsbeperkingen moeten worden gecontroleerd
- de tag <form name="unFormulaire"> dient om de integriteitsbeperkingen van een specifiek formulier te definiëren, namelijk het formulier waarvan de naam wordt opgegeven door het attribuut name. Deze naam moet voorkomen in de lijst met formulieren die zijn gedefinieerd in struts-config.xml. Hier wordt het gebruikte formulier frmPersonne gedefinieerd in struts-config.xml door de volgende sectie:
<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>
- Een <form>-tag bevat evenveel <field>-tags als er integriteitsregels voor het formulier moeten worden gecontroleerd. Een <field>-tag heeft de volgende attributen:
- property: naam van het formulierveld waarvoor integriteitsregels worden gedefinieerd
- depends: lijst met te controleren integriteitsregels.
- De mogelijke beperkingen zijn: required (het veld mag niet leeg zijn), mask (de waarde van het veld moet overeenkomen met een reguliere uitdrukking die wordt gedefinieerd door de variabele mask), integer: de waarde van het veld moet een geheel getal zijn, byte (byte), long (lang geheel getal), float (enkelvoudig drijvende komma), double (dubbele drijvende komma), short (kort geheel getal), date (de waarde van het veld moet een geldige datum zijn), range (de waarde van het veld moet binnen een bepaald bereik liggen), email: (de waarde van het veld moet een geldig e-mailadres zijn), ...
- de integriteitsbeperkingen worden gecontroleerd in de volgorde van het attribuut `depends`. Als een beperking niet wordt geverifieerd, worden de volgende niet getest.
- Elke beperking is gekoppeld aan een foutmelding die wordt gedefinieerd door een sleutel. Hier volgen enkele voorbeelden in de vorm beperking (sleutel): required (errors.required), mask (errors.invalid), integer (errors.integer), byte (errors.byte), long (errors.long), ...
- de foutmeldingen die bij de voorgaande sleutels horen, zijn gedefinieerd in het bestand validator-rules.xml:
# Foutmeldingen van 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.
- Hierboven zien we dat de berichten in het Engels zijn. Bovendien staan ze als opmerkingen in het bestand, waarin wordt aangegeven dat ze in het berichtenbestand van de applicatie moeten worden geplaatst. Ter herinnering: dit wordt gedefinieerd in een sectie van het bestand struts-config.xml:
Het attribuut **parameter** geeft aan dat de berichten van de applicatie zich in het bestand
WEB-INF/classes/ressources/personneressources.properties.
De foutmeldingen moeten dus in dit bestand worden geplaatst. Ze worden toegevoegd aan de reeds bestaande:
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>
# Foutmeldingen van Struts Validator
# de sleutel is vooraf gedefinieerd en mag niet worden gewijzigd
# het bijbehorende foutbericht is vrij
# het bericht kan maximaal 4 parameters hebben: {0} tot {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
Laten we de integriteitsbeperkingen van het bestand validation.xml een voor een bekijken om ze uit te leggen:
<formset>
<form name="frmPersonne">
<field property="nom" depends="required">
<arg0 key="personne.nom"/>
</field>
...
</form>
</formset>
Ter herinnering: deze integriteitsvoorwaarden zijn van toepassing op de velden 'naam' en 'leeftijd' van een dynamisch formulier dat is gedefinieerd in 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>
Het is belangrijk dat de namen van het formulier en de velden daarin in beide bestanden identiek zijn. De integriteitsbeperking voor het veld ‘naam’ (property="naam") bepaalt dat het veld niet leeg mag zijn (depends="required"). Als dit niet het geval is, wordt een object ActionError met sleutel errors.required gegenereerd. Het bericht dat bij deze sleutel hoort, staat in het bestand personne.ressources.properties:
We zien dat dit bericht een parameter {0} gebruikt. De waarde hiervan wordt bepaald door de tag <arg0> van de integriteitsbeperking:
Ook hier wordt arg0 aangeduid door een sleutel die eveneens in het berichtenbestand te vinden is:
Als we dit allemaal aan elkaar plakken, is het foutbericht dat wordt gegenereerd als het veld ‘naam’ niet is ingevuld:
Laten we nu de tweede voorwaarde analyseren, die voor het veld ‘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>
Er zijn twee geldigheidsregels voor het veld 'age': 'required' en 'mask'. We kunnen de vorige uitleg herhalen voor de geldigheidsregel 'required'. Dit leidt ertoe dat de foutmelding die bij deze geldigheidsregel hoort, als volgt luidt:
De tweede beperking is mask. Dit betekent dat de inhoud van het veld moet voldoen aan een patroon dat wordt uitgedrukt door een reguliere expressie. De waarde hiervan wordt gedefinieerd in een <var>-tag, waarin een variabele met de naam mask (<var-name>) wordt gedefinieerd met als waarde ${positiefgetal} (<var-value>). positiefgetal is een constante die in de <global>-sectie van het bestand is gedefinieerd met als waarde de reguliere expressie ^\s*\d+\s*$. De integriteitsvoorwaarde is dus dat de leeftijd een reeks van één of meerdere cijfers moet zijn, eventueel voorafgegaan of gevolgd door spaties. Als niet aan deze voorwaarde wordt voldaan, wordt een object ActionError met sleutel errors.invalid gegenereerd. In het berichtenbestand wordt deze sleutel gekoppeld aan het volgende bericht:
De beperking moet een waarde voor de parameter {0} definiëren. Dit gebeurt met de tag <arg0>:
In het berichtenbestand is de sleutel personne.age gekoppeld aan het volgende bericht:
De foutmelding die wordt gegenereerd als niet aan de mask-beperking wordt voldaan, is dus:
5.3. De klassen van de applicatie
In een Struts-toepassing moeten de klassen voor de formulieren (ActionForm of daarvan afgeleid) en voor de acties (Action of daarvan afgeleid) worden geschreven. In de nieuwe toepassing strutspersonne2 is er geen klasse meer voor het formulier. De inhoud van het formulier wordt gedefinieerd in struts-config.xml en de bijbehorende integriteitsregels in WEB-INF/validation.xml. In de applicatie strutspersonne1 zag de klasse FormulaireAction voor de verwerking van het formulier er als volgt uit:
package istia.st.struts.personne;
....
public class FormulaireAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
// we hebben een geldig formulier, anders zouden we hier niet zijn gekomen
DynaActionForm formulaire=(DynaActionForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//uitvoeren
}
De klasse FormulaireAction verwachtte een formulier in de vorm van een object DynaActionForm (code in kader). In het bestand struts-config.xml is de klasse van het formulier echter als volgt gedefinieerd:
<form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
...
</form-bean>
Het formulier wordt dus in een object van het type DynaValidatorForm geplaatst. Deze klasse blijkt afgeleid te zijn van de klasse DynaActionForm. De methode execute van onze klasse FormulaireAction blijft dus geldig. Deze hoeft niet opnieuw te worden geschreven.
5.4. Implementatie en testen van de applicatie strutspersonne2
5.4.1. De context aanmaken
We hebben deze nieuwe applicatie strutspersonne2 genoemd. We maken een nieuwe definitie aan in het Tomcat-bestand <tomcat>\conf\serveur.xml van Tomcat 4.x:
Zodra dit is gebeurd, moet Tomcat opnieuw worden opgestart. We kunnen de geldigheid van de context controleren door de URL op te vragen:
http://localhost:8080/strutspersonne2/

5.4.2. De weergaven
We kopiëren de map ‘vues’ van de applicatie strutspersonne1 naar de map van de applicatie strutspersonne2. De weergaven zijn namelijk niet veranderd.

5.4.3. De map WEB-INF
We kopiëren de map WEB-INF van de applicatie strutspersonne1 naar de map van de applicatie strutspersonne2. Enkele bestanden zijn gewijzigd:

Het configuratiebestand struts-config.xml ziet er nu als volgt uit:
<?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>
Dit bestand is identiek aan dat van de applicatie strutspersonne1, met uitzondering van de dynamische definitie van het formulier en de toevoeging van de validatieplugin (omkaderd).
In WEB-INF voegen we het volgende validatiebestand validation.xml toe:
<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>
We voegen in WEB-INF de bestanden validator-rules.xml en validator-rules_1_1.dtd toe, die te vinden zijn in <struts>\lib:

In de map WEB-INF/classes staat nu nog maar één klasse:

In de map WEB-INF\classes\ressources staat het volgende berichtenbestand 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>
# Foutmeldingen van Struts Validator
# de sleutel is vooraf gedefinieerd en mag niet worden gewijzigd
# de bijbehorende foutmelding is vrij
# de melding kan maximaal 4 parameters hebben {0} tot {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

5.5. Tests
We zijn klaar voor de tests. Hieronder vindt u enkele schermafbeeldingen die de lezer wordt verzocht na te maken.
We vragen om de URLhttp://localhost:8080/strutspersonne2/formulaire.do:

We gebruiken de knop [Envoyer] zonder de velden in te vullen:

We proberen het opnieuw, maar er treedt een fout op in het veld ‘age’:

We krijgen het volgende antwoord:

We proberen het nog eens, maar voeren deze keer de juiste waarden in:

We krijgen het volgende antwoord:

5.6. Conclusie
We hebben aangetoond dat het gebruik van dynamische formulieren met „standaard“ integriteitsbeperkingen het aanmaken van klassen om deze weer te geven overbodig maakt. Als de integriteitsbeperkingen afwijken van de standaard, moeten er opnieuw klassen worden aangemaakt om deze nieuwe beperkingen te controleren.