Skip to content

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.jarjakarta-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 定义文件也位于该目录中:

Image

  • validation.xml 定义了应用程序各种动态表单的完整性约束。该文件由开发人员创建,其名称可自定义。

这两个文件可以放置在 WEB-INF 目录下的任意位置。在本示例中,它们将直接放置在 WEB-INF 目录下:

Image

5.2. 为动态表单编写完整性约束

validation.xml 文件将包含针对类型为 org.apache.struts.validator.DynaValidatorFormfrmPersonne 表单中 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 文件的某个部分中进行了定义:
    <message-resources 
      parameter="ressources.personneressources"
    null="false"
  />    

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 文件中:

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

我们可以看到,该消息使用了 {0} 参数。其值由完整性约束中的 <arg0> 标签设定:

             <arg0 key="personne.nom"/>      

同样,arg0 由一个键来指定,该键也出现在消息文件中:

   personne.nom=nom

综合以上内容,如果未填写 name 字段,生成的错误信息为:

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

现在让我们来分析第二个约束条件,即针对“年龄”字段的约束:

    <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>

年龄字段有两个约束条件:必填和输入格式。关于必填约束,我们可以重复之前的解释。这意味着与该约束相关的错误信息将是:

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

第二个约束是掩码。这意味着该字段的内容必须匹配由正则表达式表示的模式。 该约束的值定义在 <var> 标签中,该标签定义了一个名为 mask (<var-name>) 的变量,其值为 ${positiveInteger} (<var-value>)。positiveInteger 是文件 <global> 部分中定义的常量,其值为正则表达式 ^\s*\d+\s*$。 因此,完整性约束要求年龄必须是一串一个或多个数字,其前后可选地带有空格。如果未满足此约束,将生成一个具有 errors.invalid 键的 ActionError 对象。在消息文件中,该键关联以下消息:

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

该约束必须为 {0} 参数定义一个值。它使用 <arg0> 标签来实现:

             <arg0 key="personne.age"/>      

在消息文件中,键 person.age 与以下消息相关联:

     personne.age=age

因此,如果未验证掩码约束,将生成以下错误消息:

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

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 文件中创建一个新的定义:

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

完成上述操作后,必须重启 Tomcat。您可以通过访问以下 URL 来验证该上下文的有效性:

http://localhost:8080/strutspersonne2/

Image

5.4.2. 视图

strutspersonne1 应用程序中的 views 文件夹复制到 strutspersonne2 应用程序文件夹中。视图文件未发生变化。

Image

5.4.3. WEB-INF 文件夹

strutspersonne1 应用程序中的 WEB-INF 文件夹复制到 strutspersonne2 应用程序文件夹中。其中有几个文件发生了变化:

Image

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.xmlvalidator-rules_1_1.dtd 文件添加到 WEB-INF 目录中:

Image

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

Image

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

Image

5.5. 测试

我们已准备好进行测试。以下是一些截图,欢迎读者尝试重现。

请访问网址 http://localhost:8080/strutspersonne2/formulaire.do

Image

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

Image

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

Image

我们收到以下响应:

Image

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

Image

我们得到以下响应:

Image

5.6. 结论

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