6. HTML 表单
到目前为止,我们使用了一个仅包含两个输入字段的单一表单。在此,我们建议使用标准的图形组件(单选按钮、复选框、输入字段、下拉列表、列表)来创建和处理表单。
6.1. 应用程序视图
该应用程序仅包含两个视图。第一个视图显示一个空白表单:
视图 1 - 表单

这个名为 form.jsp 的第一个视图,将让我们能够实现 struts-html 库中的各种标签。用户填写表单:

[提交]按钮用于确认已输入的值。这将作为第二个视图:

在第二个视图中,我们将使用另外两个标签库:struts-bean 和 struts-logic。[返回表单] 链接可让我们返回填写过表单的页面。随后,系统将带我们回到第一个视图。
6.2. 应用程序架构
![]() |
- 该表单(视图 1)将由一个名为 dynaFormulaire 的动态 Struts 对象表示,该对象是 DynaActionForm 的子类。它将通过 form.jsp 视图进行显示。
- Struts 动作 InitFormulaireAction 将负责检索显示表单所需的数据
- 填写完成的表单将由一个 ForwardAction 进行处理,该操作仅将请求重定向至第二个视图 confirmation.jsp。该视图将负责显示表单的值。
6.3. 应用程序配置
6.3.1. server.xml 文件
应用程序上下文将命名为 /formulaire2。因此,我们将向 Tomcat 的 server.xml 文件中添加以下行:
完成此操作后,可能需要重启 Tomcat 以便其识别新的上下文。我们可以通过访问 URL http://localhost:8080/formulaire2 来验证其有效性:

6.3.2. web.xml 文件
应用程序的 web.xml 配置文件如下所示:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
</web-app>
与我们之前看到的 web.xml 配置文件相比,我们做了一些改动:
- 我们将引入两个新的标签库:struts-bean 和 struts-logic。它们将在 confirmation.jsp 视图中使用。另一方面,formulaire.jsp 视图将使用 struts-html 库。
6.3.3. struts-config.xml 文件
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="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
</form-beans>
<action-mappings>
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
<message-resources
parameter="ApplicationResources"
null="false"/>
</struts-config>
它包含三个主要部分:
- <form-beans> 部分中的表单声明
- <action-mappings> 部分中的操作声明
- <message-resources> 部分中的资源文件声明
6.3.4. 应用程序的表单对象(Beans)
用于表示应用程序 HTML 表单的对象属于 ActionForm 类型或其派生类型(如 DynaActionForm、DynaValidatorForm 等)。它们被称为 Bean,因为其构造遵循 JavaBeans 的规则。我们的应用程序中仅有一个表单 Bean,名为 dynaFormulaire,它继承自 DynaActionForm。它将在以下情况下被使用:
- 用于存储显示视图 #1 所需的数据
- 在用户提交表单时,从视图 #1 的表单中提取值
- 用于存储显示视图 #2 所需的数据
dynaFormulaire Bean 的结构与视图 #1 中的表单紧密相关。让我们来分析一下:
![]() |
编号 | HTML 类型 | 角色 |
<input name="opt" type="radio" value="yes"> <input name="opt" type="radio" value="no"> | 一组相互关联的单选按钮(名称相同) | |
<input name="chk1" type="radio" value="on"> <input name="chk2" type="radio" value="on"> <input name="chk3" type="radio" value="on"> | 复选框组 (名字不同) | |
<input type="text" name="inputField"> | 一个文本输入框 | |
<input type="password" name="password"> | 密码输入框 | |
<textarea name="inputBox">...</textarea> | 多行输入框 | |
<select name="combo" size="1">..</select> | 下拉菜单 | |
<select name="simpleList" size="3">..</select> | 单选列表 | |
<select name="listeMultiple" size="3" multiple>..</select> | 多选列表 | |
<input type="button" value="清除" onclick='clearList("singleList")'> | 用于取消选中的按钮 simpleList(7)中选中的项目 | |
<input type="button" value="清除" onclick='clearList("multipleList")'> | 取消选中的按钮 multipleList 中选中的项目 (8) | |
<input type="submit" value="提交"> | 表单提交按钮 | |
<input type="hidden" name="secret" value="..."> | 一个隐藏字段 |
让我们考虑几种情况:
- dynaFormulaire 对象用于存储上方 HTML 表单中的值,这些值将通过 [提交] 按钮提交。因此,它必须包含与 HTML 表单相同的字段。字段类型由以下规则决定:
- 如果 HTML 字段仅提供一个值,则 dynaFormulaire 字段的类型为 java.lang.String
- 如果 HTML 字段提供多个值,则 dynaFormulaire 字段的类型为 java.lang.String[]
在上方的 HTML 表单中,只有 listeMultiple 字段可以关联多个值(即用户所选的值)。因此,**dynaFormulaire** 对象的初始定义如下:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
</form-bean>
dynaFormulaire 将如何填充由 Web 客户端发送的 HTML 表单值?
如果 HTML 字段 <input type="radio" name="opt" value="yes"> 被选中,opt 字段将接收值 "yes";如果字段 <input type="radio" name="opt" value="no"> 被选中,则接收值 "no"。 | |
如果 HTML 字段 <input name="chk1" type="radio" value="1"> 被选中,则 chk1 字段将接收值 "on";否则,它将不接收任何值。在后一种情况下,chk1 字段将保留其先前值。 | |
相同 | |
相同 | |
`champSaisie` 字段将接收用户在 HTML 字段 `<input type="text" name="champSaisie">` 中输入的文本。该文本可能是空字符串。 | |
`mdp` 字段将接收用户在 HTML 字段 `<input type="password" name="mdp">` 中输入的文本。该文本可以是空字符串。 | |
inputBox 字段将接收用户在 HTML 字段 <textarea name="inputBox">...</textarea> 中输入的文本。该文本构成一个单一字符串,包含用户输入的各行内容,行与行之间由字符序列 "\r\n" 分隔。生成的文本可选为空字符串。 | |
combo 字段将接收用户在 HTML 字段 <select name="combo" size="1">..</select> 中选定的选项。所选选项即为下拉框中显示的选项。如果选定的 HTML 选项属于 <option value="XX">YY</option> 类型,则 combo 字段将接收值 "XX"。 如果选中的 HTML 选项属于 <option>YY</option> 类型,则组合框字段将接收值 "YY"。 | |
如果存在 HTML 字段 <select name="simpleList" size="..">..</select>,则 simpleList 字段将接收用户在该字段中选定的选项。如果不存在,simpleList 字段将不接收任何值并保留其先前值。实际分配给 simpleList 字段的值遵循为下拉框指定的规则。 | |
类型为 String[] 的 listeMultiple 字段将接收用户在 HTML 字段 <select name="listeMultiple" size=".." multiple>..</select> 中选定的选项(如有)。如果没有选项,listeMultiple 数组将不接收任何值,其内容保持不变。实际赋值给 listeMultiple 数组的值遵循为下拉列表框指定的规则。 | |
secret 字段将接收来自 HTML 字段 <input type="hidden" name="secret" value="XX"> 的值 XX。该文本可选为空字符串。 |
- dynaFormulaire 对象用于为视图 #1 提供初始内容。前面的字段值将用于以下目的:
必须设置为“yes”或“no”,以便浏览器知道应选中哪个单选按钮 | |
如果 chk1 的值为“on”,复选框将被选中;否则,它将不会被选中 | |
同上 | |
相同 | |
字段值将显示在输入字段中 fieldInput | |
该字段的值将显示在 mdp 输入字段中 | |
该字段的值将显示在输入框中 inputBox | |
该字段的值指定在表单显示时,下拉列表中应选中的项目 | |
相同 | |
multipleList 数组中的值指定了在显示表单时,多选列表中应选中的项目 | |
该字段的值将被赋给 secret HTML 字段的 value 属性。 |
视图 #1 需要额外信息:
- 要在下拉列表中显示的值列表
- 要在 `listeSimple` 列表中显示的值列表
- 要在 multipleList 中显示的值列表
有多种方法可以将这些信息提供给视图。例如,将数组放入传递给视图的请求中即可。在此,我们将这些数组放入 dynaFormulaire Bean 中:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
dynaFormulaire 表单将由 /init 操作进行初始化,该操作将调用一个名为 InitFormulaireAction 的 Action 派生对象。该对象负责创建显示三个列表所需的三个数组,并将它们放入 dynaFormulaire Bean 中。配置文件将该 Bean 的作用域设置为 session。因此,Struts 控制器会将该 Bean 放入会话中。 因此,我们无需在请求-响应循环之间重新生成它。相应地,/init 动作仅会被调用一次。
- dynaFormulaire 对象还用于填充视图 #2。该视图仅用于显示数据值。
6.3.5. 应用程序操作
操作由 Action 类型或其派生类型的对象处理。操作配置在 <action-mappings> 标签内进行:
<action-mappings>
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
</action-mappings>
请注意,并非每个操作都关联有一个表单。上文中的 /affiche 操作便是如此。在详细介绍每个操作之前,让我们先回顾一下操作与表单的组合在 <action> 标签中是如何运作的:
- 一个操作始于 Web 客户端的请求,终于响应页面的发送。这就是客户端-服务器 Web 请求-响应循环。请求由类型为 ActionServlet 或其派生类的 Struts 控制器接收。该控制器也会发送响应。
- 如果尚未存在,则会创建类型为 ActionForm 或其派生类的表单 Bean。控制器会检查是否能在 scope 指定的作用域中找到名为 name 的对象。如果存在,则直接使用该对象;如果不存在,则创建该对象并将其放入 scope 指定的作用域中,同时将其与 name 指定的属性关联起来。
例如,在 /init 操作的示例中,控制器将调用 request.getSession().getAttribute("dynaFormulaire") 来确定 dynaFormulaire 是否已创建。若未创建,则通过类似 request.getSession().setAttribute("dynaFormulaire", new DynaFormulaire(...)) 的语句创建并将其添加到会话中。
- 控制器还会查找类型属性指定的 Action 对象。若未找到,则创建该对象;否则,直接使用该对象。
- 表单 Bean 的 reset 方法将被调用。该 Bean 除初始创建时外,其余情况均会被复用。因此,它可能包含您需要“清理”的数据。此操作可在 ActionForm Bean 或其派生类的 reset 方法中完成。
- 如果该操作是提交表单的目标,则客户端请求中找到的表单值会被复制到表单 Bean 中同名的字段中。请注意,复制操作之前已调用了 reset 方法。
- 如果配置中指定了 validate="true" 属性,则会调用表单 Bean 的 validate 方法。该方法必须对 Bean 中的数据进行验证。这种验证通常仅在表单刚通过提交的表单接收新数据,且您需要验证该数据有效性时发生。该方法会将错误列表通过 ActionErrors 对象返回给控制器。
- 如果 ActionErrors 对象不为空,控制器将显示由操作的 input 属性指定的视图。
- 如果不需要数据验证,或者验证成功,控制器将调用与当前操作关联的 `Action` 对象(或其派生类)的 `execute` 方法。Web 客户端的请求就是在该方法中处理的。`execute` 方法返回一个以字符串键为索引的 `ActionForward` 对象。这些键是由已配置操作的 `forward` 标签声明的。 在我们的示例中,`/init` 操作包含一个 `forward` 标签。它将键 "displayForm" 与 `form.jsp` 视图关联起来。
- 控制器会显示与接收到的键关联的视图。该视图实际上可能是一个动作,在这种情况下,上述过程将重复执行。
/init 操作
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
- /init 操作通常在用户请求 URL http://localhost:8080/formulaire2/init.do 时,于首次请求-响应循环中发生一次
- dynaForm 对象被创建或回收。它将根据 scope 属性的指定,从会话中检索(回收)或放入会话(创建)。
- 其 reset 方法被调用。它应该做什么?通常,ActionForm 对象的字段会被重置为默认值。但在本例中,我们不会这样做,因为 dynaFormulaire 对象被放置在会话中(scope="session")。因此,dynaFormulaire 的字段必须保留其值。在初始创建 dynaFormulaire 对象时,这些值是什么?有两种情况:
- 字段在配置文件中指定了初始值:
在这种情况下,Struts 控制器将使用该初始值创建该字段。
- 如果配置中未为该字段指定初始值:则适用 Java 的初始化规则。通常,数值字段的初始值为零,字符串字段的初始值为空字符串,其他对象的初始值为 null。
让我们来看一下 dynaFormulaire 的初始配置:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
<form-property name="chk1" type="java.lang.String"/>
<form-property name="chk2" type="java.lang.String"/>
<form-property name="chk3" type="java.lang.String"/>
<form-property name="champSaisie" type="java.lang.String" initial=""/>
<form-property name="mdp" type="java.lang.String" initial=""/>
<form-property name="boiteSaisie" type="java.lang.String" initial=""/>
<form-property name="combo" type="java.lang.String"/>
<form-property name="listeSimple" type="java.lang.String"/>
<form-property name="listeMultiple" type="java.lang.String[]"/>
<form-property name="secret" type="java.lang.String" initial="xxx"/>
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
创建后,dynaFormulaire 字段的初始值如下:
字段 | 初始值 |
"no" | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串 | |
空字符串数组 | |
"xxx" | |
空字符串数组 |
- 人们可能会认为,dynaFormulaire 的 reset 方法会为这三个数组赋值,从而填充 formulaire.jsp 视图中的三个列表。这在理论上是可行的,因为这三个数组中的数据是任意生成的。 然而,最常见的情况是这些数据来自应用程序模型,即 MVC 中的 M。在此,为了简化示例,我们将采取折中方案,由 InitFormulaireAction 动作(即 MVC 中的 C)生成这些值。
- 在 dynaFormulaire 中无需编写 reset 方法,因为其继承的 ActionForm 类本身已包含一个不执行任何操作(不进行初始化)的 reset 方法。
- 一旦调用 dynaFormulaire 的 reset 方法,控制器会检查该操作的 validate 属性。此时,该属性的值为“false”。因此,dynaFormulaire 的 validate 方法将不会被调用。
- 系统会创建 InitFormulaireAction 对象(若已存在则复用),并调用其 execute 方法。该方法会向 dynaFormulaire 的三个数组(valeursCombo、valeursListeSimple 和 valeursListeMultiple)赋值。该方法返回一个键为 "afficherFormulaire" 的 ActionForward 对象。
- 控制器显示 /vues/formulaire.jsp 视图,该视图已通过 /init 动作中的 forward 标签与“afficherFormulaire”键相关联。
/confirmation 操作
<action
path="/confirmation"
name="dynaFormulaire"
validate="false"
scope="session"
parameter="/vues/confirmation.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- 当用户点击视图 #1 上的 [提交] 按钮时,将触发 /confirmation 操作。随后,浏览器会将用户填写的表单“提交”至 Struts 控制器。
- dynaFormulaire 对象将从会话中检索
- 其 reset 方法被调用。一旦该方法被调用,Struts 控制器就会将客户端提交的表单字段值复制到 dynaFormulaire 中同名的字段中。让我们回顾一下 dynaFormulaire 中的字段列表,看看这种复制是如何进行的:
字段 | 关联的 HTML 代码 | 字段值 表单值复制后的状态 |
<input type="radio" name="opt" value="yes">是 <input type="radio" name="opt" value="no" checked="checked">否 | - 根据所选单选按钮,返回“yes”或“no” | |
<input type="checkbox" name="chk1" value="on"> | - 若已勾选 chk1 复选框,则为 "on" - 若未勾选 chk1 复选框,则保留其先前值 | |
<input type="checkbox" name="chk2" value="on"> | - 若 chk2 复选框已被选中,则为 "on" - 如果 chk2 复选框未被选中,则保留其先前值 | |
<input type="checkbox" name="chk2" value="on"> | - 若 chk3 复选框已被选中,则为 "on" - 如果未勾选 chk3 复选框,则保留其先前值 | |
<input type="text" name="inputField" value=""> | - 用户在 inputField 中输入的值 | |
<input type="password" name="password" value=""> | - 用户在 password 字段中输入的值 | |
<textarea name="inputBox"></textarea> | - 用户在文本框中输入的值 | |
<select name="combo">...</select> | - 用户在下拉列表中选择的值 | |
<select name="simpleList" size="3">...</select> | - 用户在 simpleList 中选择的值 | |
<select name="listeMultiple" multiple="multiple" size="5"> | - 包含用户在 multipleList 中所选值的字符串数组 | |
<input type="hidden" name="secret" value="xxx"> | - "xxx"。 |
我们遇到一个问题:某些字段在浏览器发送的请求中未必会收到值。这适用于复选框 chk1 至 chk3 以及两个列表 listeSimple 和 listeMultiple。在这种情况下,这些字段会保留其先前值——即在上一次请求-响应循环中获取的值。
以复选框 chk1 为例,假设在之前的请求-响应循环中,用户已勾选了该复选框。随后,浏览器在其请求的参数字符串中发送了 chk1="on" 这一信息。因此,构造函数将值 "on" 赋给了 dynaFormulaire 的 chk1 字段。现在假设在当前循环中,用户未勾选 chk1 复选框。 在这种情况下,在新请求的参数字符串中,浏览器不会发送类似 chk1="off" 的内容,而是什么也不发送。结果,dynaFormulaire 中的 chk1 字段将保留其 "on" 值,因此其值无法反映用户提交表单时的实际状态。 我们将使用 dynaFormulaire 的 reset 方法来解决此问题。在此方法中,我们将把 chk1、chk2 和 chk3 这三个字段设置为“off”。在我们的 chk1 示例中,用户要么:
- 勾选 chk1 复选框。此时,浏览器会发送 chk1="on" 的信息,dynaFormulaire 中的 chk1 字段将变为 "on"
- 未勾选 chk1 复选框。此时浏览器不会发送 chk1 字段的值,该字段将保留其之前的值“off”。无论哪种情况,dynaFormulaire 中 chk1 字段存储的值都是正确的。
对于 simpleList 和 multiList 列表,情况类似。如果这些列表中未选中任何选项,它们将不会出现在请求参数中,因此会保留其先前值。在 dynaFormulaire 的 reset 方法中,我们将使用空字符串重置 simpleList,并使用长度为 0 的字符串数组重置 multiList。
- 一旦调用 dynaFormulaire 的 reset 方法,控制器会将客户端请求中发送给它的信息复制回 dynaFormulaire 的字段中
- 创建或复用一个 ForwardAction 对象,并调用其 execute 方法。ForwardAction 是一个预定义类,它返回一个 ActionForward 对象,该对象指向由动作的“parameter”属性定义的视图,在本例中即 /vues/confirmation.jsp。
- 控制器发送此视图。循环完成。
/display
<action
path="/affiche"
parameter="/vues/formulaire.jsp"
type="org.apache.struts.actions.ForwardAction"
/>
- 点击视图 #2 上的 [返回表单] 链接将触发 /display 操作。
- 此处该操作未关联任何表单。因此,我们将直接执行 `ForwardAction` 对象的 `execute` 方法,该方法将返回一个指向视图 `/vues/formulaire.jsp` 的 `ActionForward` 对象。
6.3.6. 应用程序的消息文件
struts-config.xml 文件的第三部分是消息文件:
ApplicationResources.properties 文件位于 WEB-INF/classes 目录下。该文件为空。尽管文件为空,仍必须在配置文件中声明它;否则,我们稍后将讨论的 struts-bean 标签库会报错。该库由 confirmation.jsp 视图使用。
6.4. 视图代码
6.4.1. formulaire.jsp 视图
请记住,该视图在以下两种情况下显示:
- 在首次请求-响应循环中调用 /init 操作时
- 在后续请求-响应循环中调用 /affiche 操作时
formulaire.jsp 视图的代码如下:
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>formulaire</title>
</head>
<body background='<html:rewrite page="/images/standard.jpg"/>'>
<h3>Formulaire Struts</h3>
<hr>
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<table border="0">
<tr>
<td>bouton radio</td>
<td>
<html:radio name="dynaFormulaire" property="opt" value="oui">Oui</html:radio>
<html:radio name="dynaFormulaire" property="opt" value="non">Non</html:radio>
</td>
</tr>
<tr>
<td>Cases à cocher</td>
<td>
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox>
<html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox>
<html:checkbox name="dynaFormulaire" property="chk3">3</html:checkbox>
</td>
</tr>
<tr>
<td>Champ de saisie</td>
<td>
<html:text name="dynaFormulaire" property="champSaisie" />
</td>
</tr>
<tr>
<td>Mot de passe</td>
<td>
<html:password name="dynaFormulaire" property="mdp" />
</td>
</tr>
<tr>
<td>Boîte de saisie multilignes</td>
<td>
<html:textarea name="dynaFormulaire" property="boiteSaisie" />
</td>
</tr>
<tr>
<td>Combo</td>
<td>
<html:select name="dynaFormulaire" property="combo">
<html:options name="dynaFormulaire" property="valeursCombo"/>
</html:select>
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td>Liste à sélection unique</td>
</tr>
<tr>
<td>
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
</td>
</tr>
</table>
<td>
<html:select name="dynaFormulaire" property="listeSimple" size="3">
<html:options name="dynaFormulaire" property="valeursListeSimple"/>
</html:select>
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td>Liste à sélection multiple</td>
</tr>
<tr>
<td>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
</td>
</tr>
</table>
</td>
<td>
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="valeursListeMultiple"/>
</html:select>
</td>
</tr>
</table>
<html:hidden name="dynaFormulaire" property="secret"/>
<br>
<hr>
<html:submit>Envoyer</html:submit>
</html:form>
</body>
</html>
此 JSP 页面使用了 struts-html 标签库中的标签。请记住,要使用标签库,您必须:
- 在应用程序的 web.xml 文件中使用 <tag-lib> 标签进行声明
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
- 将此库的代码放置在应用程序目录树的某个位置,此处为 WEB-INF/struts-html.tld
- 在使用该库的 JSP 页面开头声明使用该库:
formulaire.jsp 视图中使用了以下标签,下面我们将对此进行说明:
<body background="<html:rewrite page="/images/standard.jpg"/>"> | |||
html:rewrite 标签允许您在 URL 中省略应用程序名称。它有一个属性:
因此,在上例中,如果您决定将应用程序命名为“form3”,则背景属性的代码无需重写。`html:rewrite` 标签将生成新的 HTML 代码 background="/formulaire3/images/standard.jpg" |
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire"> | |||||||
html:form 标签生成 HTML 表单标签。它具有以下几个属性:
我们可以看到,默认情况下,生成的 HTML 代码使用 POST 方法。在同一段 HTML 代码中,action 的 URL 已被重写,使其前缀为应用程序名称,后缀为 .do。 |
<html:radio name="dynaFormulaire" property="opt" value="yes">是</html:radio> | |||||||
html:radio 标签用于生成 HTML 标签 <input type="radio" ...>。它支持多种属性:
开始标签和结束标签之间的文本将显示在单选按钮旁边。 |
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox> | |||||||
html:checkbox 标签用于生成 HTML 标签 <input type="checkbox" ...>。它支持多种属性:
开始标签和结束标签之间的文本将显示在复选框旁边。 |
<html:text name="dynaFormulaire" property="inputField" /> | |||||||
html:text 标签用于生成 HTML 标签 <input type="text" ...>。它支持多种属性:
|
<html:password name="dynaFormulaire" property="mdp" /> | |
html:password 标签用于生成 HTML 标签 <input type="password" ...>。它支持多种属性: |
<html:textarea name="dynaFormulaire" property="boiteSaisie" /> | |||||||
HTML:textarea 标签用于生成 HTML 标签 <textarea>...</textarea>。它支持多种属性:
|
<html:select name="dynaFormulaire" property="combo">....</html:select> | |||||||
HTML 的 :select 选择器用于生成 HTML 标签 <select>...</select>。它支持多种属性:
|
<html:select name="dynaFormulaire" property="combo"> <html:options name="dynaFormulaire" property="comboValues"/> </html:select> | |||||
HTML:options 标签用于在 HTML <select> 标签内部生成 <option>...</option> 这些 HTML 标签。指定如何查找用于填充 select 元素的值有多种方法。在此,我们使用了 name 和 property 属性:
|
另外两个列表的生成方式与前一个类似:
<html:select name="dynaFormulaire" property="listeSimple" size="3">
<html:options name="dynaFormulaire" property="valeursListeSimple"/>
</html:select>
在上文中,我们指定了一个不为 1 的 size 属性,以创建列表而非下拉框。
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true">
<html:options name="dynaFormulaire" property="valeursListeMultiple"/>
</html:select>
在上文中,我们指定了 multiple="true" 属性,以创建一个支持多选的列表。
<html:hidden name="dynaFormulaire" property="secret"/> | |||||
html:hidden 标签用于生成 HTML 标签 <input type="hidden" ...>。
|
要充分理解 formulaire.jsp 视图与其在内存中对应的 dynaFormulaire Bean 之间的关系,必须记住 dynaFormulaire Bean 既用于读取,也用于写入:
![]() |
当用户点击表单上的 [提交] 按钮时,会触发请求。随后,浏览器将 HTML 表单“POST”到 /confirmation 操作。我们之前已经解释过此时会发生什么,特别是 dynaFormulaire 字段将接收 HTML 表单中同名字段的值。
当控制器响应请求时要求显示 formulaire.jsp 页面,会发生什么?让我们逐一仔细查看这些标签:
<body background="<html:rewrite page="/images/standard.jpg"/>"> | |
生成 HTML 代码 |
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire"> ... </html:form> | |
生成 HTML 代码 |
<html:radio name="dynaFormulaire" property="opt" value="yes">是</html:radio> <html:radio name="dynaFormulaire" property="opt" value="no">否</html:radio> | |
如果 dynaFormulaire 的 opt 字段为 "yes",则生成 HTML 代码 |
<html:checkbox name="dynaFormulaire" property="chk1">1</html:checkbox> <html:checkbox name="dynaFormulaire" property="chk2">2</html:checkbox> <html:checkbox name="dynaForm" property="chk3">3</html:checkbox> | |
如果 dynaFormulaire 的 chk1 和 chk3 字段为“on”,而 chk2 字段为“off”,则生成 HTML 代码 |
<html:text name="dynaFormulaire" property="inputField" /> | |
如果输入字段的值设置为“this is a test”,则生成 HTML 代码 |
<html:password name="dynaFormulaire" property="password" /> | |
如果密码字段为“azerty”,则生成 HTML 代码 |
<html:password name="dynaFormulaire" property="mdp" /> | |
如果“mdp”字段为“azerty”,则生成 HTML 代码 |
<html:password name="dynaFormulaire" property="mdp" /> | |
如果“mdp”字段为“azerty”,则生成 HTML 代码 |
<html:select name="dynaFormulaire" property="combo"> <html:options name="dynaFormulaire" property="comboValues"/> </html:select> | |
如果下拉列表字段为“combo2”,则生成 HTML 代码 |
<html:select name="dynaFormulaire" property="simpleList" size="3"> <html:options name="dynaFormulaire" property="simpleListValues"/> </html:select> | |
如果 simpleList 字段为 "simple1",则生成 HTML 代码 |
<html:select name="dynaFormulaire" property="listeMultiple" size="5" multiple="true"> <html:options name="dynaFormulaire" property="multipleListValues"/> </html:select> | |
如果 listeMultiple 字段是一个包含 {"multiple0", "multiple2"} 的数组,则生成以下 HTML 代码 |
<html:hidden name="dynaFormulaire" property="secret"/> | |
如果 secret 字段的值为 "xxx",则生成 HTML 代码 |
<html:submit>提交</html:submit> | |
生成 HTML 代码 |
最后要说明的是 JSP 页面中包含的 JavaScript 代码,该代码与两个 [清除] 按钮相关联,用于取消选中 simpleList 和 multipleList 列表中的选定项:
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
该标签
<html:form action="/confirmation" name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
会生成以下 HTML 代码:
为了理解与 [清除] 按钮相关的 JavaScript 代码,让我们回顾一下在使用该文档的 JavaScript 代码中,如何引用 Web 文档的各个元素:
数据 | 含义 |
指整个网页文档 | |
指文档中定义的表单集合 | |
表示文档中的第 i 个表单 | |
指名称属性设置为“formName”的 <form> 表单 | |
指名称属性等于“formName”的 <form> 表单 | |
指由表达式 [form] 指定的表单所属的元素集合。该集合包含指定表单中的所有 <input>、<textarea> 和 <select> 标签。 | |
指 [form] 中的第 i 个元素 | |
指 [form] 中 name 属性等于 componentName 的元素 | |
指 [form] 中 name 属性等于 componentName 的元素 | |
指当 [form] 表单的 [component] 组件的 HTML 代码可能包含 value 属性(<input>、<textarea>)时,该组件的值 | |
指列表中选中选项的索引。可用于读取和写入。将此属性设置为 -1 将取消选中列表中的所有项目。 | |
指与 <select> 标签关联的选项数组 | |
指指定 <select> 标签中的第 i 个选项 | |
一个布尔值,表示指定 [select] 元素的第 i 个选项是否被选中(true)。可用于读取和写入 |
让我们重新审视这两个按钮的 JavaScript 代码:
<input type="button" value="Effacer" onclick="this.form.listeSimple.selectedIndex=-1"/>
<input type="button" value="Effacer" onclick="this.form.listeMultiple.selectedIndex=-1"/>
当点击按钮时,与“onclick”属性关联的代码会被执行。这里是内联代码。通常,我们会写成 onclick="function(...)",其中 function 是在 <script language="javascript">...</script> 标签内定义的函数。上面的代码起什么作用?让我们来分析第一个按钮的代码:
指代包含该按钮的网页文档 | |
指包含该按钮的表单 | |
指表单中的 simpleList 组件 | |
表示在 listeSimple 中所选选项的索引。将此属性设置为 -1 可取消选中所有选项。 |
6.4.2. confirmation.jsp 视图
请注意,该视图在 /confirmation 操作之后显示,即在 Web 客户端提交 formulaire.jsp 视图中的表单之后。其唯一目的是显示用户输入的值。其代码如下:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head>
<title>Confirmation</title>
</head>
<body background="<html:rewrite page="/images/standard.jpg"/>">
<h3>Confirmation des valeurs saisies</h3>
<hr/>
<table border="1">
<tr>
<td>Bouton radio</td>
<td><bean:write name="dynaFormulaire" scope="session" property="opt"/></td>
</tr>
<tr>
<td>Case à cocher chk1</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk1"/></td>
</tr>
<tr>
<td>Case à cocher chk2</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk2"/></td>
</tr>
<tr>
<td>Case à cocher chk3</td>
<td><bean:write name="dynaFormulaire" scope="session" property="chk3"/></td>
</tr>
<tr>
<td>Champ de saisie</td>
<td><bean:write name="dynaFormulaire" scope="session" property="champSaisie"/></td>
</tr>
<tr>
<td>Mot de passe</td>
<td><bean:write name="dynaFormulaire" scope="session" property="mdp"/></td>
</tr>
<tr>
<td>Boîte de saisie</td>
<td><bean:write name="dynaFormulaire" scope="session" property="boiteSaisie"/></td>
</tr>
<tr>
<td>combo</td>
<td><bean:write name="dynaFormulaire" scope="session" property="combo"/></td>
</tr>
<tr>
<td>liste simple</td>
<td><bean:write name="dynaFormulaire" scope="session" property="listeSimple"/></td>
</tr>
<logic:iterate id="choix" indexId="index" name="dynaFormulaire" property="listeMultiple">
<tr>
<td>liste multiple[<bean:write name="index"/>]</td>
<td><bean:write name="choix"/></td>
</tr>
</logic:iterate>
</table>
<br>
<html:link page="/affiche.do">
Retour au formulaire
</html:link>
</body>
</html>
在此,我们将介绍两个新的标签库:struts-bean 和 struts-logic。struts-bean 库提供了访问请求、会话或应用程序上下文中对象的途径。struts-logic 库允许您使用标签来实现执行逻辑。这两个库都不是绝对必要的。正如我们所见,一个 JSP 页面可以:
- 从请求(request.getAttribute(...))、会话(session.getAttribute(...))或应用程序上下文中检索对象
- 使用变量 <%= 变量 %> 在 HTML 代码中插入动态元素
- 包含 Java 代码 <% Java 代码 %>
在 JSP 页面中包含 Java 代码,让那些希望严格区分应用逻辑(Java 代码)与呈现(标签的使用)的人感到困扰。正因如此,才为他们创建了标签库。
我们将按照处理 formulaire.jsp 视图时的做法进行,并对 confirmation.jsp 代码中出现的每个标签进行说明(如果这些标签在 formulaire.jsp 视图中尚未出现过)。首先,请注意该页面开头声明了将要使用的三个标签库:
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
另请注意,这三个库必须在应用程序的 web.xml 文件中进行声明。接下来我们将对 formulaire.jsp 文档中的标签进行说明:
将值写入当前的 HTML 流。bean:write 标签支持以下属性: name:要使用的对象名称 scope:用于查找该对象的作用域(request、session、context) property:指定要写入其属性的、由 name 标识的对象的字段。该字段可以是任何类型的对象。将使用该对象的 toString 方法。 此处将写入 dynaFormulaire 中 opt 字段的值。结果将根据用户所选单选按钮的 value="yes" 或 value="no" 属性,分别显示为 "yes" 或 "no" |
将 dynaFormulaire 中 chk1 字段的值写入该字段。如果用户勾选了复选框,结果为“on”;否则为“off”。chk2 和 chk3 同样适用。 |
将 dynaFormulaire 中 champSaisie 字段的值写入,即用户在该字段中输入的文本。mdp 和 boiteSaisie 同样适用。 |
将 dynaFormulaire 中下拉列表字段的值写入。该值即为用户选中的 <option> 元素的 value 属性值。 |
将 dynaForm 中 simpleList 字段的值写入。如果用户选择了某个 <option> 元素,该值将包含该元素的 value 属性;否则,该值为空字符串。 |
在此,我们将介绍逻辑标签。我们正在处理一个多选列表。dynaFormulaire 对象的 listeMultiple 字段的值是一个字符串数组。在 Java 中,我们会编写一个循环。logic:iterate 标签允许我们在不编写 Java 代码的情况下执行相同的循环。在此示例中,logic:iterate 标签具有以下属性: name="dynaFormulaire":要使用的对象名称 property="listeMultiple":指定由 name 标识的对象中,包含循环迭代集合的属性名称。此处该集合即为 listeMultiple 中选中的值数组。该数组可能为空。 id="choix":标识符,用于在每次迭代中指定数组的当前元素。在第一次迭代中,choix 将表示 listeMultiple[0];在第二次迭代中表示 listeMultiple[1],依此类推。 indexID="index": 标识符,用于在每次循环迭代中指定当前数组元素的索引。在第一次迭代中,index 的值为 0;在第二次迭代中,值为 1;以此类推。 位于 <logic:iterate ...> 和 </logic:iterate> 标签之间的 HTML 代码将针对由 (name,property) 对指定的集合中的每个元素重复执行。该代码的动态部分如下:
根据前文所述,在第 i 次迭代(i>=0)时,生成的 HTML 代码等同于以下内容: |
会生成一个相对于应用程序上下文的链接,从而无需了解具体上下文。该标签生成的 HTML 代码如下: |
6.5. Java 类
struts-config.xml 配置文件引用了两个 Java 类:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
...
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
DynaFormulaire 类是用于存储来自视图 #1(formulaire.jsp)的值的类。InitFormulaireAction 类是用于处理通过 formulaire.jsp 上的 [提交] 按钮提交的表单值的类。
6.5.1. DynaFormulaire 类
要保存表单的值,一个 DynaActionForm 类型的对象就足够了,除非你需要重写该类的 reset 或 validate 方法之一。 在此,由于未进行数据验证,因此无需重写 validate 方法。但必须重写 reset 方法。这是因为 DynaFormulaire 对象的字段将从 Web 客户端提交的表单中获取其值。然而,如果某些字段在请求中不存在,它们可能无法获得值。这种情况发生在以下情形中:
- 用户未勾选的复选框
- 包含多个选项但未被选中的下拉列表
对于包含此类组件的表单,必须重写 reset 方法
- 将与该复选框关联的字段值设置为“off”
- 将空字符串赋值给与单选列表关联的字段
- 将一个空字符串数组赋值给与多选列表关联的字段
因此,如果这些字段未从查询中获取值,它们将保留由 reset 赋值的值,该值对应于用户验证表单时组件的状态(复选框未勾选,列表中未选中任何项目)。
作为 DynaActionForm 的子类,DynaFormulaire 类的代码如下:
package istia.st.struts.formulaire;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;
public class DynaFormulaire extends DynaActionForm {
public void reset(ActionMapping mapping, HttpServletRequest request){
// reset checkboxes - value off
set("chk1","off");
set("chk2","off");
set("chk2","off");
// reset listeSimple - empty string
set("listeSimple","");
// reset listeMultiple - empty table
set("listeMultiple",new String[]{});
}
}
6.5.2. InitFormAction 类
InitFormAction 类与 struts-config.xml 文件中的 /init 动作相关联:
<action
path="/init"
name="dynaFormulaire"
validate="false"
scope="session"
type="istia.st.struts.formulaire.InitFormulaireAction"
>
<forward name="afficherFormulaire" path="/vues/formulaire.jsp"/>
</action>
/init 操作仅在 DynaForm 对象的初始化过程中调用一次。其目的是为表单的三个下拉列表(simpleList、multipleList 和 dropdownList)提供内容。这些内容以三个数组的形式提供,这些数组是 dynaForm 对象的属性:
<form-bean name="dynaFormulaire" type="istia.st.struts.formulaire.DynaFormulaire">
<form-property name="opt" type="java.lang.String" initial="non"/>
...
<form-property name="valeursCombo" type="java.lang.String[]" />
<form-property name="valeursListeSimple" type="java.lang.String[]" />
<form-property name="valeursListeMultiple" type="java.lang.String[]"/>
</form-bean>
一旦 valuesCombo、valuesSingleList 和 valuesMultipleList 这三个数组被 InitFormAction 初始化后,就不再需要再次初始化。这是因为 dynaForm 对象被放置在会话中,因此会在请求-响应循环中保留其值。这就是为什么 /init 操作只执行一次。InitFormAction 的代码如下:
package istia.st.struts.formulaire;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class InitFormulaireAction
extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
// prepares the form to be displayed
// we put the information needed for the form in its bean
DynaFormulaire formulaire = (DynaFormulaire) form;
formulaire.set("valeursCombo", getValeurs(5, "combo"));
formulaire.set("valeursListeSimple", getValeurs(7, "simple"));
formulaire.set("valeursListeMultiple", getValeurs(10, "multiple"));
// we give back
return mapping.findForward("afficherFormulaire");
} //execute
// list of combo values
private String[] getValeurs(int taille, String label) {
String[] valeurs = new String[taille];
for (int i = 0; i < taille; i++) {
valeurs[i] = label + i;
}
return valeurs;
}
}
- 该类继承自 Action 类。这是强制要求的。
- Struts 控制器通过其 execute 方法使用 Action 对象。因此,必须重写此方法。该方法接收以下参数:
- ActionMapping mapping:一个表示 struts-config.xml 中应用程序配置的对象
- ActionForm form:与该 Action 关联的表单,前提是在 Action 的配置中(即 Action 的 name 属性)定义了该表单。
- HttpServletRequest request:客户端请求
- HttpServletResponse:发回给客户端的响应
- InitFormulaireAction 类必须初始化 dynaFormulaire 表单。该表单作为 ActionForm 表单参数传递给 execute 方法。请注意,dynaFormulaire 的类型为 DynaFormulaire,该类继承自 DynaActionForm 类,而 DynaActionForm 类又继承自 ActionForm 类。
- 在 execute 方法中,使用 DynaActionForm 类的 set 方法为 valeursCombo、valeursListeSimple 和 valeursListeMultiple 这三个字段赋值。为简化起见,这些值是任意的数组。 请注意,set 方法用于为现有字段赋值,无法用于创建新字段。因此,必须在 struts-config.xml 文件中定义 dynaFormulaire 对象时,预先声明 valeursCombo、valeursListeSimple 和 valeursListeMultiple 这三个字段。
- execute 方法最后将要显示的视图的键作为响应返回给控制器。这里是 afficherFormulaire 键,该键在 struts-config.xml 文件中已与 /vues/formulaire.jsp 视图相关联。
6.6. 部署
应用程序的目录结构如下:
![]() | ![]() |
![]() | ![]() |


请注意,上面的 ApplicationResources.properties 文件是 struts-bean 标签库所必需的。我们知道该文件包含应用程序的消息,这些消息可供 struts-bean 库访问。在此,我们的应用程序未定义任何消息。因此,ApplicationResources.properties 文件虽然存在,但内容为空。
6.7. 结论
在本课中,我们详细介绍了如何管理 HTML 表单的各个组件。现在,我们可以在 Struts 应用程序中使用复杂的表单了。






