Skip to content

9. Web 应用程序 MVC [personne] – 版本 4

此版本使用了前文介绍的 JSTL 标签库。

9.1. Eclipse 项目

要创建 Web 应用程序 [/personne4] 的 Eclipse 项目 [mvc-personne-04],需按照第 78 页第 6.2 节所述步骤复制项目 [mvc-personne-03]。

9.2. Web 应用程序 [personne4] 的配置

应用程序 /personne4 的文件 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-04</display-name>
    ...

该文件与上一版本完全相同,仅第 6 行有所变化,其中 Web 应用程序的显示名称已更改为 [mvc-personne-04]。

[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="/main"/>
  • 第 5 行:页面 [index.jsp] 将客户端重定向至 URL [/main],该 URL 指向应用程序 [/personne4] 的控制器 [ServletPersonne]。 <c:redirect> 标签属于 JSTL / Core 库。其特点在于通过添加以下内容来补充其 [url] 属性的 URL:
  • 前缀 [/contexte],其中 [contexte] 是应用程序的上下文,此处为 [personne4]。
  • 后缀 [?jsessionid=id_session](如果发起请求的浏览器未发送会话 Cookie)。标识符 [jsessionid] 表示 Web 服务器发送给客户端的会话令牌标识符。它取决于 Web 服务器。 此处为 Tomcat 服务器的标识符。[id_session] 即会话令牌本身。

因此,第5行中[index.jsp]的真实重定向URL是[/personne4/main?jsessionid=XX],其中XX是会话令牌。 下图即为初始请求 URL [http://localhost:8080/personne4] 后所显示的页面:

Image

<c:redirect>标签与会话令牌之间的关联相当微妙。现在探讨这个问题为时尚早,但我们日后会有机会再回来讨论。

9.3. 视图代码

9.3.1. 视图 [formulaire]

该视图未发生变化:

Image

它由以下页面生成: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">
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>

<html>
  <head>
    <title>Personne - formulaire</title>
    <script language="javascript">
...
      </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>

新增内容:

  • 第4行:声明JSTL / Core标签库
  • 第21、25行:在页面模板中获取[nom, age]属性。 需要提醒的是,这些属性将依次在页面 JSP 的请求 [request]、会话 [session] 以及应用程序 [application] 中进行查找。 在本例中,控制器将把它们放入会话中。
  • 由于上述操作,页面 JSP 的开头不再包含用于获取页面模板的 Java 代码。

9.3.2. 视图 [reponse]

该视图未发生变化:

新页面 JSP [reponse.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">
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>

<html>
    <head>
      <title>Personne</title>
  </head>
  <body>
      <h2>Personne - réponse</h2>
    <hr>
    <table>
        <tr>
          <td>Nom</td>
        <td>${nom}</td>
      </tr>
        <tr>
          <td>Age</td>
        <td>${age}</td>
      </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>

新增内容:

  • 第4行:声明JSTL / Core标签库
  • 第16、20、28行:在页面模板中获取[nom, age, lienRetourFormulaire]属性。页面开头不再包含用于获取该属性的Java代码JSP。

9.3.3. 视图 [erreurs]

该视图未发生变化:

新页面 JSP [erreurs.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">
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>

<html>
    <head>
      <title>Personne</title>
  </head>
  <body>
      <h2>Les erreurs suivantes se sont produites</h2>
    <ul>
            <c:forEach var="erreur" items="${erreurs}">
                <li>${erreur}</li>
            </c:forEach>
    </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>

新增内容:

  • 第4行:声明JSTL / Core标签库
  • 第13-15行:使用JSTL显示错误列表
  • 页面开头不再包含用于获取页面模板的 Java 代码 JSP。

9.4. 视图测试

为了对上述视图进行测试,我们将它们的页面 JSP 复制到 Eclipse 项目的 /WebContent/JSP 文件夹中:

Image

然后在 JSP 文件夹中,对页面进行如下修改:

[formulaire.jsp]:


...
<%
  // -- 测试:创建页面模板
  session.setAttribute("nom","tintin");
  session.setAttribute("age","30");
%>

<html>
  <head>

已添加第 4-5 行以创建页面所需的模板。

[reponse.jsp]:


...
<%
  // -- 测试:创建页面模板
  request.setAttribute("nom","milou");
  request.setAttribute("age","10");
  request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>


<html>
  <head>

...

已添加第 4-6 行以创建页面所需的模板。

[erreurs.jsp]:



<%
  // -- 测试:创建页面模板
  ArrayList<String> erreurs1=new ArrayList<String>();
  erreurs1.add("erreur1");
  erreurs1.add("erreur2");
  request.setAttribute("erreurs",erreurs1);
  request.setAttribute("lienRetourFormulaire","Retour au formulaire");
%>

<html>
  <head>

已添加第 4-8 行以创建页面所需的模板。

如果尚未运行,请启动 Tomcat,然后请求以下 URL:

 

我们确实获得了预期的视图。

9.5. 控制器 [ServletPersonne]

Web 应用程序 [/personne3] 中的控制器 [ServletPersonne] 将处理以下操作:

编号
请求
来源
处理
1
[GET /personne4/main]
用户输入的URL
- 发送空的视图 [formulaire]
2
[POST /personne4/main]
带参数 [txtNom,
txtAge,
action=validationFormulaire]
已发布
点击按钮
[Envoyer] 视图中的
[formulaire]
- 检查参数值 [txtNom, txtAge]
- 若参数值不正确,则发送视图 [erreurs(erreurs)]
- 若参数正确,则发送视图 [reponse(nom,age)]
3
[POST /personne4/main]
带参数
[action=retourFormulaire]
已发布
点击链接 [返回
表单] 链接
[réponse] 和 [erreurs]。
- 发送已预填入最新输入值的视图 [formulaire]

控制器 [ServletPersonne] 的框架与上一版本完全相同。我们将重点审查对方法 [doInit, doValidationFormulaire, doRetourFormulaire] 所做的修改,而方法 [init, doGet, doPost] 保持不变。

9.5.1. 方法 [doInit]

该方法处理第 1 号请求 [GET /personne4/main]。其代码如下:

1
2
3
4
5
6
    // 显示空表单
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
}

新增内容:

  • 第3行:显示视图[formulaire]。该视图在其模板中期待“name”和“age”这两个属性。但它无法找到这些属性,因为控制器并未将它们传递进去。 在这种情况下,库 JSTL 将为这些属性获取 null 的指针。 这不会引发错误,视图 [formulaire] 中的 ${nom} ${age} 元素将显示为空值。这正是我们所期望的。 这样,我们就无需初始化视图 [formulaire] 的模型。

9.5.2. 方法 [doValidationFormulaire]

该方法处理第 2 号请求 [POST /personne4/main],其提交数据中包含 [action, txtNom, txtAge]。其代码如下:

    // 表单验证
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
                // 获取参数
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
        // 并将参数存储在会话中
        HttpSession session = request.getSession(true);        
        session.setAttribute("nom", nom);
        session.setAttribute("age", age);
        // 验证参数
...
        // 参数正确 - 发送响应页面
        request.setAttribute("lienRetourFormulaire", (String)params.get("lienRetourFormulaire"));
        getServletContext().getRequestDispatcher((String)params.get("urlReponse")).forward(request,
                response);
        return;
    }

更新内容:

  • 第 15 行:方法 [doValidationFormulaire] 返回视图 [réponse]。该视图的模板中包含元素 [nom, age, lienRetourFormulaire]。 [lienRetourFormulaire] 通过请求在第 14 行被放入模板中。 而 [nom,age] 元素则是通过会话,在第 8-10 行放入模型中的。在之前的版本中,当发送 [réponse] 视图时,我们也将 [nom, age] 元素放入了请求中,因为该视图正是在那里等待这些元素的。 在此,借助 JSTL 库,我们知道系统将遍历不同上下文(请求、会话、应用程序)以查找模型中的元素。因此,这些元素将在会话中被找到,因为控制器已将其放入会话中(第 8-10 行)。

9.5.3. 方法 [doRetourFormulaire]

该方法处理第3号请求 [POST /personne4/main],其提交数据中包含 [action=retourFormulaire]。其代码如下:

1
2
3
4
5
6
7
    // 显示预填充表单
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // 显示表单
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

更新内容:

  • 第 4 行:显示视图 [formulaire]。该视图在其模型中等待“姓名”和“年龄”属性。 它将在会话中找到这些属性,因为方法 [doValidationFormulaire] 已将其放入会话中,且该方法必然在方法 [doRetourFormulaire] 之前执行。因此,在第 4 行显示 [formulaire] 之前,无需初始化其模型。 因此,方法 [doInit] 和 [doRetourFormulaire] 完全相同,我们可以删除操作 [retourFormulaire],并用操作 [init] 代替。 届时,方法 [doRetourFormulaire] 将被移除。

9.6. 测试

在此新版本中,仅视图发生了变化。控制器 [ServletPersonne] 本身并未改变。使用 JSTL 仅仅是为了让我们能够在 JSP 页面中更简便地利用控制器构建的模型。

将Eclipse项目[personne-mvc-04]集成到Tomcat后,启动或重启Tomcat。访问网址[http://localhost:8080/personne4]。