10. Web应用程序 MVC [personne] – 版本 5
10.1. 简介
在此版本中,我们进行了两项修改:
第一项修改涉及客户端向服务器指示所需操作的方式。 此前,该操作是通过客户端的 GET 或 POST 请求中名为 [action] 的参数来指定的。 在此,操作将由客户端请求的URL的最后一个元素指定,如下所示:

在 [1] 中,表单提交的 URL 为 [/personne5/do/validationFormulaire]。正是 URL 的最后一个元素 [validationFormulaire] 使控制器能够识别应执行的操作。 在 [2] 中,由链接 [Retour au formulaire] 触发的 POST 操作是在 URL [/personne5/do/retourFormulaire] 上执行的。 同样,URL中的最后一个元素[retourFormulaire]向控制器指示了应执行的操作。
我们引入这一修改,是因为这是 Struts 或 Spring MVC 等最流行的 Web 开发框架所采用的方法。
应用程序中的所有 URL 都将采用 [/personne5/do/action] 的形式。应用程序 [/personne5] 中的 [web.xml] 文件将表明该应用程序接受 [/do/*] 形式的 URL:
<servlet-mapping>
<servlet-name>personne</servlet-name>
<url-pattern>/do/*</url-pattern>
</servlet-mapping>
控制器将按以下方式获取要执行的操作名称:
[request] 对象的 [getPathInfo] 方法返回请求 URL 的最后一个元素。
第二项修改涉及在两个请求/响应周期之间如何保存用户输入。目前,这些信息存储在会话中。如果用户数量众多且每个用户需要存储的数据量很大,这种方法可能会带来不便。实际上,每个用户都有自己的个人会话。 此外,除非特意提供注销选项,否则会话在用户离开后仍会保持活跃一段时间。因此,1000个各占100字节的会话将占用1MB内存。这仍属于中等需求,且很少有应用程序会同时保持1000个会话处于活动状态。
不过,除了会话之外,还有其他更节省内存的替代方案,了解这些方案很有必要。这里我们将使用Cookie的方法。让我们通过一个例子来说明。
步骤1:用户提交表单:
![]() |
这一请求/响应循环会在客户端与服务器之间产生以下数据交换:
[1] : [demande du client]
这是一个典型的 POST 请求。此处没有特别需要说明的地方,只是虽然我们不会使用会话,但 Web 服务器还是创建了一个会话。这可以从第 11 行浏览器发回给服务器的会话令牌中看出,该令牌是它之前从服务器接收到的。
[2]:[réponse du serveur]
可以看到,第3行和第4行中,HTTP和[Set-Cookie]这两个头部字段已被发送至客户端浏览器,其中一个用于存储姓名(第3行),另一个用于存储年龄(第4行)。 这些 Cookie 的值即为上文 POST 和 [1] 第 14 行提交的值。
步骤 2:返回表单

此请求/响应循环导致客户端与服务器之间进行以下 HTTP 数据交换:
[1]:[demande du client]
这里我们看到,点击链接 [Retour au formulaire] 触发了 POST。 第11行,我们可以看到浏览器通过Http头[Cookie]将收到的Cookie [nom, age, JSESSIONID]发回给服务器。这就是Cookie的工作原理:客户端将浏览器发送给它的Cookie发回给服务器。 在此示例中,控制器将接收 [pauline, 18] 值,并需将其填入 [txtNom, txtAge] 字段中,该字段位于 [formulaire] 视图中,该视图在 [2] 页面上显示。
[2] : [réponse du serveur]
这里没有什么特别需要指出的,除了在该响应中,服务器没有发送任何cookie。这并不会阻止浏览器在下次交互时将从服务器接收到的所有cookie发回,即使这毫无意义。因此,我们以增加客户端/服务器交互中的字符流量为代价,减轻了服务器可用内存的压力。
10.2. Eclipse 项目
要创建Web应用程序[/personne5]的Eclipse项目[mvc-personne-05],需按照第6.2节所述的步骤复制项目[mvc-personne-04]。
![]() | ![]() |
10.3. Web 应用程序 [personne5] 的配置
应用程序 /personne5 的文件 web.xml 如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>mvc-personne-05</display-name>
<!-- ServletPersonne -->
<servlet>
<servlet-name>personne</servlet-name>
<servlet-class>
istia.st.servlets.personne.ServletPersonne
</servlet-class>
...
</servlet>
<!-- 映射 ServletPersonne-->
<servlet-mapping>
<servlet-name>personne</servlet-name>
<url-pattern>/do/*</url-pattern>
</servlet-mapping>
<!-- 欢迎页面 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
该文件与上一版本完全相同,仅存在以下细微差异:
- 第 6 行:Web 应用程序的显示名称已更改为 [mvc-personne-05]
- 第 18 行:应用程序处理的 URL 格式为 [/do/*]。此前仅处理 URL [/main]。现在,URL 的数量与待处理的操作数量相同。
首页 [index.jsp] 发生变化:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<c:redirect url="/do/formulaire"/>
- 第 5 行:页面 [index.jsp] 将客户端重定向至 URL [/personne5/do/formulaire],这相当于要求控制器执行操作 [formulaire]。
10.4. 视图代码
[formulaire, réponse, erreurs]视图的变化不大。唯一的改动在于,待执行的操作不再像以前那样通过提交表单中名为[action]的隐藏字段来指定。 现在,该操作是在表单提交的目标 URL(c.a.d)中定义的,位于 <form> 标签的 [action] 属性中:
[formulaire.jsp]:
...
<html>
<head>
<title>Personne - formulaire</title>
<script language="javascript">
...
</script>
</head>
<body>
<center>
<h2>Personne - formulaire</h2>
<hr>
<form name="frmPersonne" action="validationFormulaire" method="post">
...
</form>
</center>
</body>
</html>
- [13] 行:表单的 [action] 参数在先前版本中消失了一段时间后,现已重新出现。要理解此处的属性值,需注意应用程序处理的所有 URL 均采用 [/do/action] 这种形式。 在 [13] 行中,[action] 属性的值为一个相对 URL(不以 / 开头)。 因此,浏览器会将其与当前显示页面的 URL 进行拼接,最终必然形成 [/do/action] 格式的 URL。 最后一个元素将被 <form> 标签中 [action] 属性的相对 URL 替换,从而使 [/do/validationFormulaire] 成为 POST 的目标。
- 隐藏字段 [action] 已消失
[réponse.jsp]:
...
<html>
...
<body>
...
<form name="frmPersonne" action="retourFormulaire" method="post">
</form>
<a href="javascript:document.frmPersonne.submit();">
${lienRetourFormulaire}
</a>
</body>
</html>
- 行 [7]:POST 的目标将变为 [/do/retourFormulaire]
- 隐藏字段 [action] 在第 7-8 行表单中已消失。
[erreurs.jsp]:
...
<html>
...
<body>
...
<form name="frmPersonne" action="retourFormulaire" method="post">
</form>
<a href="javascript:document.frmPersonne.submit();">
${lienRetourFormulaire}
</a>
</body>
</html>
- 第 [6] 行:POST 的目标将变为 [/do/retourFormulaire]
- 隐藏字段 [action] 在第 6-7 行表单中已消失。
请读者按照前几个版本中的原则测试这些新视图。
10.5. 控制器 [ServletPersonne]
Web 应用程序 [/personne5] 中的控制器 [ServletPersonne] 将处理以下操作:
请求编号 | 请求 | 来源 | 处理 |
1 | [GET /personne5/do/formulaire] | 用户输入的URL | - 发送空的视图 [formulaire] |
2 | [POST /personne5/do/validationFormulaire] 并附带参数 [txtNom, txtAge] 已发布 | 点击按钮 [Envoyer] 视图中的 [formulaire] | - 检查参数值 [txtNom, txtAge] - 若参数值不正确,则发送视图 [erreurs(erreurs)] - 若参数正确,则发送视图 [reponse(nom,age)] |
3 | [POST /personne5/do/retourFormulaire] 不带任何参数 | 点击链接 [返回 表单] 链接 [réponse] 和 [erreurs]。 | - 发送已预填入最新输入值的视图 [formulaire] |
控制器 [ServletPersonne] 的框架与上一版本完全相同。我们将回顾对方法 [doValidationFormulaire, doRetourFormulaire, doGet] 所做的修改,方法 [init, doInit, doPost] 保持不变。
10.5.1. 方法 [doGet]
方法 [doGet] 获取待执行操作的方式与前几个版本不同:
- 第 12 行:获取待执行的操作。其格式为 [/action]。
- 第 18-22 行:处理由请求 GET 调用的操作 [/formulaire]
- 第23-27行:处理由请求POST调用的操作[/validationFormulaire]
- 第 28-32 行:处理由请求 POST 调用的操作 [/retourFormulaire]
10.5.2. 方法 [doValidationFormulaire]
该方法处理第 2 个请求 [POST /personne5/do/validationFormulaire],其发送元素中包含 [txtNom, txtAge]。其代码如下:
新功能:
- 方法 [doValidationFormulaire] 返回其中一个视图 [réponse, erreurs]。 无论响应内容为何,控制器都会在其中设置两个 Cookie(第 8-9 行)。一个 Cookie 由 [Cookie] 对象表示,其构造函数接受两个参数:Cookie 的键及其关联的值。
- 第 8 行:输入的姓名值被放入键名为 "nom" 的 Cookie 中
- 第9行:年龄字段的输入值被存入键名为“age”的Cookie中
- 在使用 [response.addCookie] 方法向客户端返回 HTTP 响应时,会添加一个 Cookie。此响应目前仅处于准备状态。只有当客户端收到的视图中的 JSP 页面被执行时,该响应才会真正发送出去。
10.5.3. 方法 [doRetourFormulaire]
该方法处理第 2 号请求 [POST /personne5/do/retourFormulaire],该请求不包含任何提交的数据。其代码如下:
新功能:
方法 [doRetourFormulaire] 需显示一个预先填入最新输入内容的表单。在上一版本中,这些数据存储在会话中。而在本版本中,不再使用会话,而是使用 Cookie 来在两次客户端-服务器交互之间保存数据。 当客户端请求表单提交时,会根据具体情况收到 [réponse] 或 [erreurs] 视图作为响应,同时附带两个名为“nom”和“age”的 Cookie。 当点击这两个视图中的 [Retour au formulaire] 链接(该操作会在 URL [/do/retourFormulaire] 上触发 POST 页面)时,浏览器会将收到的两个 Cookie 发回给服务器。
- 第4-18行:获取名为“name”和“age”的Cookie的值。颇为奇怪的是,目前尚无方法能根据Cookie的键值直接获取其值。因此,我们不得不遍历所有接收到的Cookie。
- 完成上述操作后,将获取的两个值放入视图模板 [formulaire] 中(第 20-21 行),以便该视图将其显示出来。
10.6. 测试
将Eclipse项目[personne-mvc-05]集成到Tomcat后,启动或重启Tomcat,然后访问URL [http://localhost:8080/personne5]。


