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 方法,用于验证动态表单中的姓名和年龄值是否有效。需要执行两类检查:
- 两个字段均不得为空
- 年龄字段必须符合掩码(正则表达式)\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 文件将包含针对类型为 org.apache.struts.validator.DynaValidatorForm 的 frmPersonne 表单中 name 和 age 字段的完整性约束。其内容如下:
<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> 标签用于定义全局范围的信息,即在存在多个表单时对所有表单均有效。在此,我们将常量放置在 <global> 标签内。常量由其名称(<constant-name> 标签)及其值(<constant-value> 标签)定义。 我们使用正则表达式 `_^\s*\d+\s*$`(数字序列前后可能带有空格)来定义常量 `entierpositif`,该表达式用于验证正整数。
- <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>
- 一个表单标签包含的 <field> 标签数量与该表单需要检查的完整性约束数量相同。一个 <field> 标签具有以下属性:
- property:定义完整性约束的表单字段名称
- depends:待检查的完整性约束列表。
- 可能的约束条件如下:必填(该字段不能为空)、掩码(字段值必须与掩码变量定义的正则表达式匹配)、整数:字段值必须为整数、字节(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 Error Messages
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/resources/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 error messages
# the key is predefined and must not be changed
# the associated error msg is free
# the msg can have up to 4 parameters {0} to {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 中定义的动态表单中的 name 和 age 字段:
<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>
两个文件中表单及其字段的名称必须完全一致。name 字段(property="name")上的完整性约束规定该字段不能为空(depends="required")。若不满足此条件,将生成一个包含 errors.required 键的 ActionError 对象。与该键关联的消息位于 person.resources.properties 文件中:
我们可以看到,该消息使用了 {0} 参数。其值由完整性约束中的 <arg0> 标签设定:
同样,arg0 由一个键来指定,该键也出现在消息文件中:
综合以上内容,如果未填写 name 字段,生成的错误信息为:
现在让我们来分析第二个约束条件,即针对“年龄”字段的约束:
<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>
年龄字段有两个约束条件:必填和输入格式。关于必填约束,我们可以重复之前的解释。这意味着与该约束相关的错误信息将是:
第二个约束是掩码。这意味着该字段的内容必须匹配由正则表达式表示的模式。 该约束的值定义在 <var> 标签中,该标签定义了一个名为 mask (<var-name>) 的变量,其值为 ${positiveInteger} (<var-value>)。positiveInteger 是文件 <global> 部分中定义的常量,其值为正则表达式 ^\s*\d+\s*$。 因此,完整性约束要求年龄必须是一串一个或多个数字,其前后可选地带有空格。如果未满足此约束,将生成一个具有 errors.invalid 键的 ActionError 对象。在消息文件中,该键关联以下消息:
该约束必须为 {0} 参数定义一个值。它使用 <arg0> 标签来实现:
在消息文件中,键 person.age 与以下消息相关联:
因此,如果未验证掩码约束,将生成以下错误消息:
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 {
// we have a valid form, otherwise we wouldn't have got here
DynaActionForm formulaire=(DynaActionForm)form;
request.setAttribute("nom",formulaire.get("nom"));
request.setAttribute("age",formulaire.get("age"));
return mapping.findForward("reponse");
}//execute
}
FormAction 类期望接收一个以 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 4.x 的 <tomcat>\conf\serveur.xml 文件中创建一个新的定义:
完成上述操作后,必须重启 Tomcat。您可以通过访问以下 URL 来验证该上下文的有效性:
http://localhost:8080/strutspersonne2/

5.4.2. 视图
将 strutspersonne1 应用程序中的 views 文件夹复制到 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 应用程序中的文件完全相同,仅在表单的动态定义以及验证插件的引入方面有所不同(框选部分)。
将以下 validation.xml 文件添加到 WEB-INF 目录中:
<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>
将位于 <struts>\lib 目录下的 validator-rules.xml 和 validator-rules_1_1.dtd 文件添加到 WEB-INF 目录中:

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

在 WEB-INF\classes\resources 文件夹中,你会发现以下属性文件 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 error messages
# the key is predefined and must not be changed
# the associated error msg is free
# the msg can have up to 4 parameters {0} to {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. 测试
我们已准备好进行测试。以下是一些截图,欢迎读者尝试重现。
请访问网址 http://localhost:8080/strutspersonne2/formulaire.do:

我们不填写任何字段,直接点击[提交]按钮:

我们尝试在年龄字段中输入错误信息:

我们收到以下响应:

请重试,这次请输入正确的数值:

我们得到以下响应:

5.6. 结论
我们已经证明,使用带有“标准”完整性约束的动态表单可以避免创建类来表示它们。如果完整性约束偏离了标准,那么我们再次需要创建类来验证这些新的约束。