4. 动态表单的使用
4.1. 声明动态表单
我们已经看到,Struts 使用 ActionForm 对象来存储由应用程序中各个 Servlet 处理的 HTML 表单的值。对于应用程序中的每个表单,我们需要创建一个继承自 ActionForm 的类。这很快就会变得繁琐,因为对于每个字段 xx,我们都必须编写两个方法:setXx 和 getXx。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 文件中创建一个新的定义:
完成上述操作后,必须重启 Tomcat。您可以通过访问以下 URL 来验证该上下文的有效性:
http://localhost:8080/strutspersonne1/

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

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

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

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 编译的类:

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

4.5. 测试
我们已准备好进行测试。以下是一些截图,欢迎读者尝试重现。
请求 URL http://localhost:8080/strutspersonne1/formulaire.do:

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

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

我们得到以下响应:

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

我们收到以下响应:

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