7. Web 应用程序 MVC [personne] – 版本 3
[ref1]中的推荐阅读:第9章
7.1. 简介
我们计划在发送至浏览器的 HTML 页面中添加 JavaScript 代码。浏览器会执行其所显示页面中嵌入的 JavaScript 代码。 该技术与 Web 服务器用于生成 HTML 文档的技术(Java/servlets/JSP、ASP.NET、 ASP、PHP、Perl、Python 等)所使用的技术。
[formulaire.jsp]
该页面生成的视图将如下所示:

标签带有边框的按钮调用的是嵌入在页面 HTML 中的 JavaScript 代码:
标签 | 类型 HTML | 功能 |
<submit> | 相当于旧版本中的 [Envoyer] 按钮:将输入的值发送至控制器 | |
<button> | 新按钮——在将数据提交给控制器之前,先在本地验证所输入数据的有效性 | |
<reset> | 将表单恢复至浏览器最初接收时的状态 | |
<button> | 清除两个输入字段的内容 |
以下是 [Envoyer] 和 [Effacer] 按钮的使用示例:
![]() | ![]() |
![]() | ![]() |
我们还将使用 JavaScript 代码来管理视图 [erreurs] 和 [réponse] 中的链接 [Retour au formulaire]。以视图 [réponse] 为例:
![]() | 1 ![]() |
区别在于 [1] 中显示的 URL。在之前的版本中,该 URL 为:
在此,操作已不再包含在 URL 中,因为它将通过 POST 发送,而非 GET。 此修改将导致无论请求何种操作,浏览器显示的唯一 URL 均为 [http://localhost:8080/personne3/main]。
7.2. Eclipse 项目
要为 Web 应用程序 [/personne3] 创建 Eclipse 项目 [mvc-personne-03],需按照第 78 页第 6.2 节所述步骤复制项目 [mvc-personne-02]。
![]() | ![]() |
7.3. Web 应用程序 [personne3] 的配置
应用程序 /personne3 的文件 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-03</display-name>
<!-- ServletPersonne -->
<servlet>
<servlet-name>personne</servlet-name>
<servlet-class>
istia.st.servlets.personne.ServletPersonne
</servlet-class>
<init-param>
<param-name>urlReponse</param-name>
<param-value>
/WEB-INF/vues/reponse.jsp
</param-value>
</init-param>
<init-param>
<param-name>urlErreurs</param-name>
<param-value>
/WEB-INF/vues/erreurs.jsp
</param-value>
</init-param>
<init-param>
<param-name>urlFormulaire</param-name>
<param-value>
/WEB-INF/vues/formulaire.jsp
</param-value>
</init-param>
<init-param>
<param-name>lienRetourFormulaire</param-name>
<param-value>
Retour au formulaire
</param-value>
</init-param>
</servlet>
<!-- 映射 ServletPersonne-->
<servlet-mapping>
<servlet-name>personne</servlet-name>
<url-pattern>/main</url-pattern>
</servlet-mapping>
<!-- 欢迎文件 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
该文件与上一版本完全相同,仅存在以下细微差异:
- 第 6 行:Web 应用程序的显示名称已更改为 [mvc-personne-03]
参数 [urlControleur] 已消失。在上一版本中,它用于设定视图 [formulaire] 中 POST 的目标。在此新版本中,目标将设为空字符串 c.a.d。 浏览器显示的 URL。我们已说明该 URL 将始终为 [http://localhost:8080/personne3/main],这是我们为视图 [formulaire] 中的 POST 所需要的。
首页 [index.jsp] 发生变化:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%
response.sendRedirect("/personne3/main");
%>
- 第 5 行:页面 [index.jsp] 将客户端重定向至应用程序 [/personne3] 中控制器 [ServletPersonne] 的 URL。
7.4. 视图代码
7.4.1. 视图 [formulaire]
该视图已变为如下形式:

它由以下页面生成:JSP [formulaire.jsp]:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%// 从模板中获取数据
String nom = (String) session.getAttribute("nom");
String age = (String) session.getAttribute("age");
%>
<html>
<head>
<title>Personne - formulaire</title>
<script language="javascript">
// -------------------------------
function effacer(){
// 清除输入字段
with(document.frmPersonne){
txtNom.value="";
txtAge.value="";
}//配合
}//清除
// -------------------------------
function envoyer(){
// 发送前验证参数
with(document.frmPersonne){
// 名称不能为空
champs=/^\s*$/.exec(txtNom.value);
if(champs!=null){
// 名称为空
alert("Vous devez indiquer un nom");
txtNom.value="";
txtNom.focus();
// 返回可视化界面
return;
}//if
// 年龄必须为正整数
champs=/^\s*\d+\s*$/.exec(txtAge.value);
if(champs==null){
// 年龄不正确
alert("Age incorrect");
txtAge.focus();
// 返回可视化界面
return;
}//if
// 参数正确 - 将其发送至服务器
submit();
}//with
}//发送
</script>
</head>
<body>
<center>
<h2>Personne - formulaire</h2>
<hr>
<form name="frmPersonne" method="post">
<table>
<tr>
<td>Nom</td>
<td><input name="txtNom" value="<%= nom %>" type="text" size="20"></td>
</tr>
<tr>
<td>Age</td>
<td><input name="txtAge" value="<%= age %>" type="text" size="3"></td>
</tr>
<tr>
</table>
<table>
<tr>
<td><input type="submit" value="Submit"></td>
<td><input type="button" value="[Envoyer]" onclick="envoyer()"></td>
<td><input type="reset" value="Rétablir"></td>
<td><input type="button" value="[Effacer]" onclick="effacer()"></td>
</tr>
</table>
<input type="hidden" name="action" value="validationFormulaire">
</form>
</center>
</body>
</html>
更新内容:
- 第 56 行:表单名称为 [frmPersonne]。该名称将在 JavaScript 代码中使用。 请注意缺少 [action] 属性,这将导致表单 [frmPersonne] 被提交至浏览器显示的 URL。
- 第 70 行:按钮 [Submit] 起到了之前版本中按钮 [Envoyer] 的作用
- 第 71 行:点击类型为 [Button] 的按钮 [Envoyer],将执行第 24 行定义的 JavaScript 函数 [envoyer]
- 第 72 行:按钮 [Rétablir] 的功能未发生变化
- 第 73 行:点击类型为 [Button] 的按钮 [Effacer],将执行第 16 行定义的 JavaScript 函数 [effacer]
在讲解 JavaScript 代码之前,先回顾一些符号说明:
JavaScript 将显示的页面及其内容视为一个对象树,其根节点是对象 [document]。 在该文档中,可能包含一个或多个表单。[document.frmPersonne] 指代第 56 行定义的名称为 [frmPersonne] 的表单。该表单本身也包含对象。 输入字段是其中的一部分。因此,[document.frmPersonne.txtNom] 指代在第 60 行定义的输入字段 HTML 的图像对象。 对象 [txtNom] 具有多种属性,其中包括属性 [value],该属性指代输入字段的内容。因此,[document.frmPersonne.txtNom.value] 指代输入字段 [txtNom] 的内容。
- 第 16-22 行:JavaScript 函数 [effacer] 将空字符串填入输入字段 [txtNom, txtAge] 中。
- 第 24-49 行:JavaScript 函数 [envoyer] 在提交 [txtNom, txtAge] 输入字段的值之前,会使用正则表达式验证其有效性。
- 第 28 行:检查输入字段 [txtNom] 的值是否符合正则表达式 /s*,该表达式表示 0 个或多个空格。 如果答案为是,则表示用户未输入姓名。如果与模式匹配,变量 champs 的值将不同于指针 null,否则其值为 null。
- 第 29 行:如果与模板匹配
- 第 30 行:向用户显示一条消息
- 第 32 行:将空字符串填入字段 [txtNom](该字段可能包含一串空格)
- 第 33 行:将闪烁光标定位到字段 [txtNom]
- 第34行:终止函数。此时,服务器上不会有POST字段的输入值。
- 第38-45行:对字段[txtAge]执行类似操作
- 第47行:若执行至此,说明输入值正确。此时将表单[frmPersonne]提交至Web服务器。此时的处理过程与点击标有[Submit]的按钮完全相同。
7.4.2. 视图 [reponse]
当表单中输入的值有效时,此视图将显示这些值:
![]() | ![]() |
与上一版本相比,新功能仅在使用视图 [réponse] 中的链接 [Retour au formulaire] 时才会显示:
![]() | ![]() |
区别在于 [1] 中显示的 URL。在上一版本中,该 URL 为:
在此,操作已不再包含在URL中,因为它将通过POST发送,而非GET。 新页面 JSP [reponse.jsp] 是 lqqqaaaaaaaaaqqAAAaaa
如下:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%
// 从模型中获取数据
String nom=(String)request.getAttribute("nom");
String age=(String)request.getAttribute("age");
String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>
<html>
<head>
<title>Personne</title>
</head>
<body>
<h2>Personne - réponse</h2>
<hr>
<table>
<tr>
<td>Nom</td>
<td><%= nom %>
</tr>
<tr>
<td>Age</td>
<td><%= age %>
</tr>
</table>
<br>
<form name="frmPersonne" method="post">
<input type="hidden" name="action" value="retourFormulaire">
</form>
<a href="javascript:document.frmPersonne.submit();">
<%= lienRetourFormulaire %>
</a>
</body>
</html>
更新内容:
- 第 35-37 行:链接 [Retour au formulaire] 嵌入了 JavaScript。点击该链接将触发 [href] 属性中的 JavaScript 代码执行。 正如我们在研究 [formulaire.jsp] 时所见,我们知道该代码会提交表单 [frmPersonne] 中 <input>、<select> 等类型字段的值。
- 第 32-34 行:定义表单 [frmPersonne]。该表单仅包含一个 <input type="hidden" ...> 类型的字段,即隐藏字段。 该字段 [action] 用于向控制器传递待执行的操作名称,此处为 [retourFormulaire]。
7.4.3. 视图 [erreurs]
该视图用于提示表单中的输入错误。所做的修改与视图 [réponse] 相同:
![]() | ![]() |
区别在于 [1] 中显示的 URL。在之前的版本中,该 URL 为:
在此,该操作不再包含在URL中,因为它将通过POST发送,而非GET。新的页面如下:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ page import="java.util.ArrayList" %>
<%
// 获取模型数据
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>
<html>
<head>
<title>Personne</title>
</head>
<body>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
<%
for(int i=0;i<erreurs.size();i++){
out.println("<li>" + (String) erreurs.get(i) + "</li>\n");
}//用于
%>
</ul>
<br>
<form name="frmPersonne" method="post">
<input type="hidden" name="action" value="retourFormulaire">
</form>
<a href="javascript:document.frmPersonne.submit();">
<%= lienRetourFormulaire %>
</a>
</body>
</html>
更新内容:
- 第30-32行:链接点击处理机制的更新。[réponse.jsp]研究中对此的说明同样适用于此处。
7.5. 视图测试
为了对上述视图进行测试,我们将它们的页面 JSP 复制到 Eclipse 项目的 /WebContent/JSP 文件夹中:

然后在 JSP 文件夹中,对页面进行如下修改:
[formulaire.jsp]:
...
<%
// -- 测试:创建页面模板
session.setAttribute("nom","tintin");
session.setAttribute("age","30");
%>
<%// 从模板中获取数据
String nom = (String) session.getAttribute("nom");
String age = (String) session.getAttribute("age");
%>
已添加第 4-5 行以创建第 9-10 行所需的模板。
[reponse.jsp]:
...
<%
// -- 测试:创建页面模板
request.setAttribute("nom","milou");
request.setAttribute("age","10");
request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>
<%
// 从模板中获取数据
String nom=(String)request.getAttribute("nom");
String age=(String)request.getAttribute("age");
String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>
...
第 4-6 行已添加,用于创建第 11-13 行页面所需的模板。
[erreurs.jsp]:
<%
// -- 测试:创建页面模板
ArrayList<String> erreurs1=new ArrayList<String>();
erreurs1.add("erreur1");
erreurs1.add("erreur2");
request.setAttribute("erreurs",erreurs1);
request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>
<%
// 从模型中获取数据
ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");
String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>
第 4-8 行已添加,用于创建第 13-14 行页面所需的模板。
如果尚未启动,请启动 Tomcat,然后请求以下 URL:
![]() | ![]() |
![]() |
我们确实获得了预期的视图。
7.6. 控制器 [ServletPersonne]
Web 应用程序 [/personne3] 中的控制器 [ServletPersonne] 将处理以下操作:
编号 | 请求 | 来源 | 处理 |
1 | [GET /personne3/main] | 用户输入的URL | - 发送空的视图 [formulaire] |
2 | [POST /personne3/main] 带参数 [txtNom, txtAge, action=validationFormulaire] 已发布 | 点击按钮 [Envoyer] 视图中的 [formulaire] | - 检查参数值 [txtNom, txtAge] - 若参数值不正确,则发送视图 [erreurs(erreurs)] - 若参数正确,则发送视图 [reponse(nom,age)] |
3 | [POST /personne3/main] 带参数 [action=retourFormulaire] 已发送 | 点击链接 [返回 表单] [réponse] 和 [erreurs]。 | - 发送已预填入最新输入值的视图 [formulaire] |
因此,我们需要处理一个新操作:[POST /personne3/main],其提交参数为 [action=retourFormulaire],以取代旧操作 [GET /personne3/main?action=retourFormulaire]。
7.6.1. 控制器框架
控制器 [ServletPersonne] 的骨架与上一版本完全相同。
新增内容:
- 第 11 行,数组 [paramètres] 不再包含参数 [urlControleur],该参数已从文件 [web.xml] 中删除。
[init, doValidationFormulaire]方法保持不变。[doGet, doInit, doRetourFormulaire]方法略有更改。
7.6.2. 方法 [doGet]
方法 [doGet] 必须处理操作 [POST /personne3/main],并使用已提交的参数 [action=retourFormulaire]:
- 第6-9行:处理操作[POST /personne3/main],并使用已提交的参数[action=retourFormulaire]
7.6.3. 方法 [doInit]
该方法处理第1号请求 [GET /personne3/main]。其代码如下:
新变化在于,第 8 行显示的视图 [formulaire] 的模型中不再包含元素 [action]。
7.6.4. 方法 [doRetourFormulaire]
该方法处理第 1 号请求 [POST /personne3/main],其提交的元素中包含 [action=retourFormulaire]。其代码如下:
新变化在于,第 14 行显示的视图 [formulaire] 的模板中不再包含元素 [action]。
7.7. 测试
在将 Eclipse 项目 [personne-mvc-03] 集成到 Tomcat 后,启动或重启 Tomcat。访问 URL [http://localhost:8080/personne3],然后按照第 7.1 节中的示例继续进行测试。















