5. 具有完整性约束的动态表单
接下来我们将开发一个名为 strutspersonne2 的新应用程序,该应用程序使用
- 动态表单(与strutspersonne1类似)
- 一个用于声明该动态表单字段需验证的完整性约束的文件
5.1. 完整性约束的声明
在 strutspersonne1 应用程序中,用于存储姓名和年龄值的类声明如下:
<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>
我们不得不编写类 PersonneDynaForm,以便拥有一个能够验证动态表单中“姓名”和“年龄”字段值是否有效的validate方法。需要进行的验证主要有两类:
- 两个字段均不得为空
- “age”字段必须符合正则表达式模式 \s*\d+\s*
这两项验证属于 StrutsValidator 环境可执行的验证范围。该环境随 Struts 一起提供,并包含若干可在 commons-validator.jar 和 jakarta-oro.jar 中找到的类。 如果您已按照文档开头所述的步骤,在 Tomcat 中安装了 Struts 库,那么这些库在 Tomcat 中已可用。请确保它们在 JBuilder 中同样可用。这一点在文档开头也有说明。
struts-config.xml 文件中表单的新声明如下:
<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>
因此,两者几乎没有区别。动态表单关联的类现在是 StrutsValidator 的预定义类:org.apache.struts.validator.DynaValidatorForm。 开发人员无需再编写类。他只需在单独的 XML 文件中指定表单需验证的完整性约束。Struts 控制器必须知道该文件的名称。为此,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>
<plug-in> 部分用于加载 Struts 外部的类。其主要属性是 classname,用于指定要实例化的类名。实例化的对象可能需要进行初始化。这通过 set-property 标签实现,该标签有两个属性:
- property:待初始化的属性名称
- value:要初始化的属性值
在此,类 DynaValidatorForm 需要了解两项信息:
- 文件 XML,该文件定义了该类能够验证的标准完整性约束。
- 文件 XML,该文件定义了应用程序中各个动态表单的完整性约束
这两项信息由 pathnames 属性提供。该属性的值是一个文件列表,包含验证器将加载的 XML 文件:
- validator-rules.xml 是定义标准完整性约束的文件。它随 Struts 一起提供。该文件位于 <struts>\lib 目录下,并附有其定义文件 DTD:
![]()
- validation.xml 用于定义应用程序中各个动态表单的完整性约束。该文件由开发人员构建,其名称可自定义。
这两个文件可以放置在 WEB-INF 下的任意位置。在本例中,它们将直接放置在 WEB-INF 之下:

5.2. 编写动态表单的完整性约束
文件 validation.xml 将包含针对表单 frmPersonne(类型为 org.apache.struts.validator.DynaValidatorForm)中“姓名”和“年龄”字段的完整性约束。其内容如下:
<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>
需遵守的编写规则如下:
- 所有规则均位于 <form-validation> 标签内
- <global>标签用于定义全局范围的信息,c.a.d。如果存在多个表单,该定义对所有表单均有效。此处我们在<global>中定义了常量。常量由其名称(<constant-name>标签)和数值(<constant-value>标签)来定义。 我们定义常量 `entierpositif`,其值为一个正整数必须满足的正则表达式:_^\s*\d+\s*$(即数字序列,其前后可能带有空格)。
- <formset> 标签定义了需要验证完整性约束的所有表单集合
- 标签 <form name="unFormulaire"> 用于定义特定表单的完整性约束,该表单的名称由 name 属性指定。该名称必须存在于 struts-config.xml 中定义的表单列表中。 此处使用的表单 frmPersonne 在 struts-config.xml 中通过以下部分进行定义:
<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> 标签包含的 <field> 标签数量与表单中需要验证的完整性约束数量相同。一个 <field> 标签具有以下属性:
- property:为之定义完整性约束的表单字段名称
- depends:待验证的完整性约束列表。
- 可能的约束条件如下:required(字段不能为空)、mask(字段值必须符合由mask变量定义的正则表达式)、integer: 字段值必须为整数、byte(字节)、long(长整型)、float(单精度浮点数)、double(双精度浮点数)、short(短整型)、date(字段值必须为有效日期)、range(字段值必须在指定区间内)、email:(字段值必须为有效电子邮件地址),...
- 完整性约束将按 depends 属性的顺序进行验证。如果某项约束未通过验证,后续约束将不再进行测试。
- 每个约束都关联一个由键定义的错误消息。以下是部分约束的示例(格式为约束 (键)):required (errors.required)、mask (errors.invalid)、integer (errors.integer)、 byte (errors.byte)、long (errors.long)、...
- 与上述键相关的错误消息定义在文件 validator-rules.xml 中:
# 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.
- 我们可以看到,上述消息均为英文。此外,它们以注释形式出现在文件中,其中注明需将其放入应用程序的消息文件中。需要提醒的是,该消息文件在文件 struts-config.xml 的某个部分中进行了定义:
parameter 属性表明应用程序的消息位于文件
WEB-INF/classes/ressources/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>
# Struts Validator 错误消息
# 该键是预定义的,不得更改
# 关联的错误消息是空的
# 该消息最多可包含 4 个参数 {0} 至 {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
让我们逐一说明文件 validation.xml 的完整性约束:
<formset>
<form name="frmPersonne">
<field property="nom" depends="required">
<arg0 key="personne.nom"/>
</field>
...
</form>
</formset>
请注意,这些完整性约束适用于在 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>
两个文件中表单及其字段的名称必须完全一致。针对“姓名”字段(property="nom")的完整性约束规定该字段必须为非空(depends="required")。 若不满足此条件,将生成键为 errors.required 的对象 ActionError。与该键相关的消息位于文件 personne.ressources.properties 中:
可以看出,该消息使用了一个 {0} 参数。该参数的值由完整性约束中的 <arg0> 标签确定:
同样,arg0 由一个键指定,该键也出现在消息文件中:
将所有内容组合起来,如果“名称”字段未填写,生成的错误消息为:
现在我们来分析第二个约束条件,即针对 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>
“age”字段有两个约束条件:required 和 mask。我们可以再次说明“required”约束的含义。由此可知,与该约束相关的错误信息将是:
第二个约束条件是 mask。这意味着字段内容必须符合由正则表达式定义的模式。 该约束的值定义在 <var> 标签中,该标签定义了一个名为 mask (<var-name>) 的变量,其值为 ${entierpositif} (<var-value>)。entierpositif 是文件中 <global> 部分定义的常量,其值为正则表达式 ^\s*\d+\s*$。 因此,完整性约束要求年龄必须由一个或多个数字组成,其前后可带有空格。如果未满足此约束,将生成键为 errors.invalid 的对象 ActionError。 在消息文件中,该键与以下消息相关联:
该约束必须为参数 {0} 定义一个值。它通过 <arg0> 标签实现:
在消息文件中,键 personne.age 与以下消息相关联:
因此,如果未通过 mask 约束的验证,将生成以下错误消息:
5.3. 应用程序的类
在 Struts 应用程序中,需要编写的类包括表单类(ActionForm 或其派生类)和动作类(Action 或其派生类)。在新应用程序 strutspersonne2 中,已不再包含表单类。 表单内容在 struts-config.xml 中定义,相关完整性约束则在 WEB-INF/validation.xml 中定义。 在 strutspersonne1 应用程序中,表单处理类 FormulaireAction 的定义如下:
package istia.st.struts.personne;
....
public class FormulaireAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
// 表单有效,否则不会显示在此处
DynaActionForm formulaire=(DynaActionForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//执行
}
类 FormulaireAction 预期接收一个以对象 DynaActionForm(框内代码)形式呈现的表单。然而在文件 struts-config.xml 中,表单的类定义如下:
<form-bean name="frmPersonne" type="org.apache.struts.validator.DynaValidatorForm">
...
</form-bean>
因此,表单将被放置在一个类型为 DynaValidatorForm 的对象中。恰巧该类是从 DynaActionForm 类派生而来的。因此,我们 FormulaireAction 类的 execute 方法仍然有效,无需重写。
5.4. strutspersonne2 应用程序的部署与测试
5.4.1. 创建上下文
我们将这个新应用程序命名为 strutspersonne2。我们在 Tomcat 的 <tomcat>\conf\serveur.xml 文件中创建了一个新定义:
完成上述操作后,需重启 Tomcat。可通过访问 URL 来验证上下文的有效性:
http://localhost:8080/strutspersonne2/

5.4.2. 视图
我们将strutspersonne1应用程序的vues文件夹复制到strutspersonne2应用程序的文件夹中。因为视图并没有变化。

5.4.3. 文件夹 WEB-INF
我们将 strutspersonne1 应用程序中的 WEB-INF 文件夹复制到 strutspersonne2 应用程序的文件夹中。其中部分文件发生了变化:

配置文件 struts-config.xml 变为如下内容:
<?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>
该文件与应用程序 strutspersonne1 中的文件完全相同,仅在表单的动态定义和验证插件的插入方面有所不同(框选部分)。
在 WEB-INF 中添加以下验证文件 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>
在 WEB-INF 中添加位于 <struts>\lib 目录下的文件 validator-rules.xml、validator-rules_1_1.dtd:

在 WEB-INF/classes 文件夹中,现在只剩下一类:

在 WEB-INF\classes\ressources 文件夹中,可以找到以下消息文件 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>
# Struts Validator 的错误消息
# 该键已预定义,不得更改
# 关联的错误消息内容为空
# 该消息最多可包含 4 个参数 {0} 至 {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. 测试
我们已准备好进行测试。下面提供了一些屏幕截图,请读者尝试复现。
要求生成URLhttp://localhost:8080/strutspersonne2/formulaire.do:

点击按钮 [Envoyer] 而不填写字段:

重新尝试,年龄字段出现错误:

得到以下响应:

我们重新尝试,这次输入正确的值:

得到以下结果:

5.6. 结论
我们已经证明,对于具有“标准”完整性约束的动态表单,无需创建类来表示它们。如果完整性约束超出标准范围,则需要重新创建类来验证这些新约束。