Skip to content

4. 动态表单的使用

4.1. 声明动态表单

我们已经看到,Struts 使用 ActionForm 对象来存储由应用程序中各个 Servlet 处理的 HTML 表单的值。对于应用程序中的每个表单,我们需要创建一个继承自 ActionForm 的类。这很快就会变得繁琐,因为对于每个字段 xx,我们都必须编写两个方法:setXxgetXx。Struts 提供了使用表单的选项

  • ,其结构在 struts-config.xml 文件的 <form-beans> 部分中声明
  • ,这些表单将由 Struts 环境根据声明的结构动态创建

因此,在 strutspersonne 应用程序中用于存储姓名和年龄值的类可以定义如下:

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

对于表单中的每个字段,我们定义一个带有两个属性的 <form-property> 标签:

  • name:字段名称
  • type:其 Java 类型

由于 Web 客户端提交的表单值均为字符串,因此最常用的类型是:单值字段使用 java.lang.String,多值字段(如同名复选框、多选列表等)使用 java.lang.String[]。 DynactionForm 类与 ActionForm 类一样,其 validate 方法默认不执行任何操作。若要让它检查表单参数的有效性,必须继承该类并自行编写 validate 方法。因此,表单声明将如下所示:

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

我们需要自己编写 istia.st.struts.personne.PersonneDynaForm 类。

4.2. 编写与动态表单关联的 DynaActionForm 类

在上文中,我们将类 istia.st.struts.personne.PersonneDynaForm 与表单 (name, age) 关联起来。现在我们将编写该类:

package istia.st.struts.personne;

import javax.servlet.http.*;
import org.apache.struts.action.*;

public class PersonneDynaForm extends DynaActionForm {
   // validation
  public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
    // error management
    ActionErrors erreurs = new ActionErrors();
     // name must be non-empty
    String nom = (String)this.get("nom");
    if (nom == null || nom.trim().equals("")) {
      erreurs.add("nomvide", new ActionError("personne.formulaire.nom.vide"));
    }
     // age must be non-empty
    String age = (String)this.get("age");
    if (age == null || age.trim().equals("")) {
      erreurs.add("agevide", new ActionError("personne.formulaire.age.vide"));
    }
    else {
      // age must be a positive integer
      if (!age.matches("^\\s*\\d+\\s*$")) {
        erreurs.add("ageincorrect", new ActionError("personne.formulaire.age.incorrect", age));
        // return the list of errors
      }
    } //if
     // return the error list
    return erreurs;
  }
}//class

请注意以下几点:

  • 该类继承自 DynaActionForm
  • DynaActionForm 类的 validate 方法已被重写。当 Struts 控制器调用该方法时,会构建 PersonneDynaForm 对象。该对象包含一个字典,其键为表单字段 name age,值则为这些字段的实际值。要在 DynaActionForm 方法中访问字段,请使用 Object get(String fieldName) 方法。 要为字段设置值,请使用 void set(String fieldName, Object value) 方法。完整的说明请参阅 DynaActionForm 类的定义。
  • 一旦获取了表单中 name 和 age 字段的值,validate 方法就与表单关联到 ActionForm 对象时编写的方法并无二致。

4.3. 新的 FormAction 类

配置文件定义了以下 /main 操作:

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

此定义与 strutspersonne 应用程序中的定义相同。/main 动作由 FormulaireAction 类型的对象实现。该对象以前通过 FormulaireBean 类型的对象接收 frmPersonne 表单的值。现在,它通过 PersonneDynaForm 类型的对象接收这些值。因此,必须重写该类:

package istia.st.struts.personne;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.ServletException;
import istia.st.struts.personne.PersonneDynaForm;

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
    PersonneDynaForm formulaire=(PersonneDynaForm)form;
    request.setAttribute("nom",formulaire.get("nom"));
    request.setAttribute("age",formulaire.get("age"));
    return mapping.findForward("reponse");
  }//execute
}

请注意以下几点:

  • 我们必须导入包含表单数据的 PersonneDynaForm 类,才能访问其定义
  • execute 方法使用 [DynaActionForm].get 方法获取表单参数的值。

strutspersonne 应用程序中的 FormAction 类相比,仅访问表单值的方式发生了变化。

4.4. strutspersonne1 应用程序的部署与测试

4.4.1. 创建上下文

我们将这个新应用程序命名为 strutspersonne1。我们在 Tomcat 4.x 的 <tomcat>\conf\serveur.xml 文件中创建一个新的定义:

    <Context path="/strutspersonne1" docBase="e:/data/serge/web/struts/personne1" />

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

http://localhost:8080/strutspersonne1/

Image

4.4.2. 视图

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

Image

4.4.3. 编译类

我们需要创建两个类:PersonneDynaForm 和 FormulaireAction,其中后者调用前者。我们可以使用 JBuilder 项目来创建并编译它们:

Image

4.4.4. WEB-INF 文件夹

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

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

    <action-mappings>
      <action
          path="/main"
          name="frmPersonne"
            scope="session"
            validate="true"
            input="/erreurs.do"
          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"/>    
</struts-config>

该文件与 strutspersonne 应用程序中的配置文件完全相同,唯一区别在于动态表单的定义(框选部分)。

WEB-INF/classes 文件夹中,放置由 JBuilder 编译的类:

Image

WEB-INF\classes\resources 文件夹中,放置消息文件。该文件未作更改。

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 [{0}] est incorrect</li>
errors.header=<ul>
errors.footer=</ul>

Image

4.5. 测试

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

请求 URL http://localhost:8080/strutspersonne1/formulaire.do

Image

在不填写任何字段的情况下点击 [提交] 按钮:

Image

请在年龄字段中输入错误信息后重试:

Image

我们得到以下响应:

Image

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

Image

我们收到以下响应:

Image

4.6. 结论

使用动态表单可以更轻松地编写负责存储表单值的 ActionForm 类。我们可以将表单管理进一步优化。在 strutspersonne1 应用程序中,我们创建了一个 PersonneDynaForm 类来验证表单的值(姓名、年龄)。 实际上,某些验证操作经常出现:字段不可为空、字段需符合特定正则表达式、整数字段、日期字段等。此类标准验证可直接在 struts-config.html 配置文件中指定。如果需要执行的所有验证都是“标准”的,那么就没有必要为表单编写类。接下来我们将探讨这一情况。