Skip to content

6. Web应用程序 MVC[personne] – 版本 2

接下来我们将提供前一个应用程序 [/personne1] 的变体,命名为 [/personne2, /personne3, ...]。这些变体不会改变应用程序的初始架构,其架构仍如下所示:

Image

针对这些变体,我们将简化说明内容,仅介绍相较于上一版本所做的修改。

6.1. 引言

现在,我们计划为应用程序添加会话管理功能。请回顾以下要点:

  • HTTP 客户端-服务器对话是由一系列彼此独立的请求-响应序列组成的
  • 会话在同一用户的不同请求-响应序列之间起到记忆作用。如果有 N 个用户,则会有 N 个会话。

以下屏幕截图展示了当前应用程序运行时所需的效果:


交易编号 1


demande
réponse

新增内容是返回表单的链接,该链接已添加到视图 [erreurs] 中。


交流第 2 期


demande
réponse

交互1中,用户为(姓名,年龄)这对数据提供了值(xx,yy)。如果服务器在交互过程中获取了这些值,那么在交互结束时,它忘记”这些值。 然而,我们可以发现,在第2次交互中,服务器却能在响应中重新显示这些值。正是“会话”这一概念,使得Web服务器能够在后续的客户端-服务器交互过程中记住数据。当然,还有其他可能的解决方案来解决这个问题。

在第1次交互过程中,服务器会将客户端发送的(姓名,年龄)数据对存储在会话中,以便在第2次交互时显示出来。

以下是另一个在两次交互之间使用会话的示例:


交互 1


demande
réponse

新功能在于回复页面中新增了返回表单的链接。


交流第2期


demande
réponse

6.2. Eclipse 项目

为了创建 Web 应用程序 [/personne2] 的 Eclipse 项目 [mvc-personne-02],我们将复制 Eclipse 项目 [mvc-personne-01] 以保留现有内容。为此,请按以下步骤操作:

[clic droit sur projet mvc-personne-01 -> Copy]:

Image

然后是 [clic droit dans Package Explorer -> Paste]:

- 我们将新项目的名称设为 [1],并将一个已存在但为空的文件夹名称设为 [2]

此时将创建项目 [mvc-personne-02]:

Image

目前它与项目 [mvc-personne-01] 完全相同。在能够使用它之前,我们需要手动进行一些修改。进入视图 [Servers],尝试将这个新应用程序添加到 Tomcat 管理的应用程序中:

我们可以看到,在 [1] 中,Tomcat 无法识别新项目 [mvc-personne-02]。为了让 Tomcat 识别该项目,需要修改 [mvc-personne-02] 项目的配置文件。 我们使用 [File / Open File] 选项打开 [<mvc-personne-02>/.settings/.component] 文件:


<?xml version="1.0" encoding="UTF-8"?>
<project-modules id="moduleCoreId">
<wb-module deploy-name="mvc-personne-01">
<wb-resource deploy-path="/" source-path="/WebContent"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
<property name="java-output-path" value="/build/classes/"/>
<property name="context-root" value="personne1"/>
</wb-module>
</project-modules>

第 3 行指定了要在 Tomcat 中部署的 Web 模块名称。此处的名称与项目 [mvc-personne-01] 的名称相同。我们将它改为 [mvc-personne-02]:


<wb-module deploy-name="mvc-personne-02">

此外,我们还可以借此机会修改第7行中[mvc-personne-02]应用程序上下文的名称,因为它与项目[mvc-personne-01]的名称存在冲突:


<property name="context-root" value="personne2"/>

第二项修改可以在Eclipse中直接进行。但我不清楚如何在不通过配置文件的情况下完成第一项修改。

完成上述操作后,我们保存新的 [.content] 文件,然后退出并重新启动 Eclipse,以便使其生效。

Eclipse重启后,让我们尝试执行之前失败的操作:

这次,[mvc-personne-02] 项目已被成功识别。我们将它添加到配置为由 Tomcat 执行的项目中:

Image

6.3. Web 应用程序 [personne2] 的配置

应用程序 /personne2 的文件 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-02</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>urlControleur</param-name>
            <param-value>
                main
            </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-02]
  • 第 31-36 行:定义了名为 [urlControleur] 的配置参数,该参数指向 URL [main],该 URL 指向 Servlet [ServletPersonne]
  • 第 37-42 行:定义了一个名为 [lienRetourFormulaire] 的配置参数,该参数是返回表单的链接文本,来自页面 JSP、[erreurs.jsp] 和 [reponse.jsp]。

主页 [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("/personne2/main");
%>
  • 第 5 行:页面 [index.jsp] 将客户端重定向至应用程序 [/personne2] 的控制器 [ServletPersonne] 的 URL。

6.4. 视图代码

6.4.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">
<%
    // 从模板中提取数据
  String nom=(String)session.getAttribute("nom");
  String age=(String)session.getAttribute("age");
  String urlAction=(String)request.getAttribute("urlAction");
%>
    
<html>
    <head>
      <title>Personne - formulaire</title>
  </head>
  <body>
      <center>
        <h2>Personne - formulaire</h2>
      <hr>
      <form action="<%=urlAction%>" 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="Envoyer"></td>
            <td><input type="reset" value="Rétablir"></td>
            <td><input type="button" value="Effacer"></td>
          </tr>
        </table>
        <input type="hidden" name="action" value="validationFormulaire"> 
      </form>
    </center>
  </body>
</html>

更新内容:

  • 第19行,表单现在有一个名为 [action] 的属性,其值为当用户点击类型为 submit 的 [Envoyer] 按钮时,浏览器应将表单值提交到的 URL。 变量 [urlAction] 的值将变为 action="main"。在用户执行以下操作后,将显示视图 [formulaire]:
  • 初始请求:GET /personne2/main
  • 点击链接 [Retour au formulaire]:GET /personne2/main?action=retourFormulaire

由于 [action] 属性未指定绝对 URL(以 / 开头),而是指定了相对 URL(不以 / 开头),因此浏览器将使用当前显示页面 [/personne2] 的 URL 前缀,并在此基础上附加该相对 URL。 因此,POST 的 URL 将变为 [/personne2/main],即控制器对应的 URL。该请求 POST 将携带第 23、27 和 38 行中的参数 [txtNom, txtAge, action]。

  • 第8行:获取模型中[urlAction]元素的值。该值在当前请求的属性中查找,将在第19行使用。
  • 第6-7行:获取模型中[nom, age]元素的值。这些值在会话属性中查找,而非像上一版本那样在查询属性中查找。 此举旨在满足视图 [réponse] 和 [erreurs] 链接中 [GET /personne2/main?action=retourFormulaire] 请求的需求。 在显示这两个视图之前,控制器会将表单中输入的数据存入会话,从而在用户使用视图 [réponse] 和 [erreurs] 的链接 [Retour au formulaire] 时,能够检索到这些数据。

6.4.2. 视图 [reponse]

当表单中输入的值有效时,该视图将显示这些值:

与上一版本相比,新增内容来自链接 [Retour au formulaire]。该视图由以下页面生成: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">


<%
    // 获取模型数据
  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>
    <a href="?action=retourFormulaire"><%= lienRetourFormulaire %></a>
  </body>
</html>
  • 第31行:返回表单的链接。该链接包含两个部分:
    • 目标 [href="?action=retourFormulaire"]。视图 [réponse] 显示在表单 [formulaire.jsp] 的 POST 之后,其 URL 为 [/personne2/main]。 因此,当显示视图 [réponse] 时,浏览器中显示的正是该最后一个 URL。 点击链接 [Retour au formulaire] 时,浏览器将向该链接的 [href] 属性指定的 URL 发起 GET 请求,此处为 "?action=retourFormulaire"。 如果 [href] 中没有 URL,浏览器将使用当前显示视图的 URL,即 c.a.d。[/personne2/main]。 最终,点击链接 [Retour au formulaire] 将触发浏览器向 URL [/personne2/main?action=retourFormulaire] 发送 GET 请求, c.a.d 作为应用程序控制器的 URL,并附带参数 [action] 来指示其应执行的操作。
    • 链接的文本。该文本将作为控制器传递给页面的模板的一部分,并在第 10 行被提取。

6.4.3. 视图 [erreurs]

该视图用于报告表单中的输入错误:

与上一版本相比,新功能来自链接 [Retour au formulaire]。该视图由以下页面生成: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">
<%@ 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>
    <a href="?action=retourFormulaire"><%= lienRetourFormulaire %></a>
  </body>
</html>

  • 第26行:返回表单的链接。该链接与视图[réponse]中的链接完全相同。建议读者重新阅读该视图的相关说明。

6.5. 视图测试

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

Image

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

[formulaire.jsp]:


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

<%
    // 从模板中获取数据
  String nom=(String)session.getAttribute("nom");
  String age=(String)session.getAttribute("age");
  String urlAction=(String)request.getAttribute("urlAction");
%>

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

[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:

 

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

6.6. 控制器 [ServletPersonne]

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

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

因此,我们需要处理一个新的操作:[GET /personne2/main?action=retourFormulaire]。

6.6.1. 控制器框架

控制器[ServletPersonne]的骨架与上一版本几乎完全相同:

package istia.st.servlets.personne;

...
import javax.servlet.http.HttpSession;

@SuppressWarnings("serial")
public class ServletPersonne extends HttpServlet {
    ...

    // 初始化
    @SuppressWarnings("unchecked")
    public void init() throws ServletException {
        ...
    }

    @SuppressWarnings("unchecked")
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
...
    }

    // 显示空表单
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        ...
    }

    // 显示预填充表单
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        ...
    }

    // 表单验证
    void doValidationFormulaire(HttpServletRequest request,
    ...
    }

    // 提交
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    ...
    }
}

新增内容:

  • 第 4 行:使用会话需要导入 [HttpSession] 包
  • 第 28-30 行:新方法 [doRetourFormulaire] 处理新操作:[GET /personne2/main?action=retourFormulaire]。

6.6.2. [init] 控制器的初始化

方法 [init] 与上一版本完全相同。它会检查文件 [web.xml] 中是否包含数组 [paramètres] 中声明的元素:

1
2
3
4
5
6
public class ServletPersonne extends HttpServlet {
    // 实例参数
    private String urlErreurs = null;
    private ArrayList erreursInitialisation = new ArrayList<String>();
    private String[] paramètres={"urlFormulaire","urlReponse","urlControleur","lienRetourFormulaire"};
  private Map params=new HashMap<String,String>();
  • 第 5 行:已添加参数 [urlControleur](控制器 URL)和 [lienRetourFormulaire](视图 [réponse] 和 [erreurs] 的链接文本)。

6.6.3. 方法 [doGet]

方法 [doGet] 需处理此前不存在的操作 [GET /personne2/main?action=retourFormulaire]:

        @SuppressWarnings("unchecked")
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

        // 检查Servlet初始化情况
        if (erreursInitialisation.size() != 0) {
            // 跳转至错误页面
            request.setAttribute("erreurs", erreursInitialisation);
            request.setAttribute("lienRetourFormulaire", "");
            getServletContext().getRequestDispatcher(urlErreurs).forward(
                    request, response);
            // 结束
            return;
        }
        // 获取发送请求的方法
        String méthode=request.getMethod().toLowerCase();
        // 获取待执行的操作
        String action=request.getParameter("action");
        // 操作?
        if(action==null){
            action="init";
        }
        // 执行操作
        if(méthode.equals("get") && action.equals("init")){
            // 启动应用程序
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("validationFormulaire")){
            // 验证输入表单
            doValidationFormulaire(request,response);
            return;
        }
        if(méthode.equals("get") && action.equals("retourFormulaire")){
            // 返回输入表单
            doRetourFormulaire(request,response);
            return;
        }
        // 其他情况
        doInit(request,response);
    }
  • 第 6-14 行:检查初始化错误列表是否为空。若非如此,则显示视图 [erreurs(erreursInitialisation)] 以报告相关错误。

要理解此代码,需回顾视图 [erreurs] 的模板:


<%
// 从模板中获取数据
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); 
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>

视图 [erreurs] 期望在请求中包含一个名为“errors”的键值对。控制器在第 8 行创建了该键值对。它还期望包含一个名为“lienRetourFormulaire”的键值对。控制器在第 9 行创建了该键值对。此处的链接文本将为空。 因此,在发送的 [erreurs] 视图中将不会包含链接。实际上,如果应用程序初始化时出现错误,则必须重新配置该应用程序。此时无需通过链接提示用户继续使用应用程序。

  • 第 34-37 行:处理新操作 [GET /personne2/main?action=retourFormulaire]

6.6.4. 方法 [doInit]

该方法处理第 1 号请求 [GET /personne2/main]。针对此请求,它必须发送空视图 [formulaire(nom,age)]。其代码如下:

    // 显示空表单
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // 获取用户会话
        HttpSession session = request.getSession(true);        
        // 提交空表单
        session.setAttribute("nom", "");
        session.setAttribute("age", "");
        request.setAttribute("urlAction", (String)params.get("urlControleur"));
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }
  • 第 4 行:若当前会话存在则获取,否则创建(来自 getSession 的参数 true)。
  • 第 9-10 行:显示视图 [formulaire]。回顾该视图所需的模板:

<%
    // 从模型中获取数据
  String nom=(String)session.getAttribute("nom");
  String age=(String)session.getAttribute("age");
  String urlAction=(String)request.getAttribute("urlAction");
%>
  • 第 6-7 行:将视图 [formulaire] 模型中的 [nom,age] 元素初始化为空字符串,并将其放入会话中,因为视图正是在此处等待这些元素。
  • 第 8 行:模型中的 [urlAction] 元素使用文件 [web.xml] 中 [urlControleur] 参数的值进行初始化,并放入请求中。

6.6.5. 方法 [doValidationFormulaire]

该方法处理第 2 号请求 [POST /personne2/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);
        // 验证参数
        ArrayList<String> erreursAppel = new ArrayList<String>();
        // 姓名字段不能为空
        nom = nom.trim();
        if (nom.equals(""))
            erreursAppel.add("Le champ [nom] n'a pas été rempli");
        // 年龄必须为大于等于0的整数
        if (!age.matches("^\\s*\\d+\\s*$"))
            erreursAppel.add("Le champ [age] est erroné");
        // 参数有错误吗?
        if (erreursAppel.size() != 0) {
            // 发送错误页面
            request.setAttribute("erreurs", erreursAppel);
            request.setAttribute("lienRetourFormulaire", (String)params.get("lienRetourFormulaire"));
            getServletContext().getRequestDispatcher(urlErreurs).forward(
                    request, response);
            return;
        }
        // 参数正确 - 发送响应页面
        request.setAttribute("nom", nom);
        request.setAttribute("age", age);
        request.setAttribute("lienRetourFormulaire", (String)params.get("lienRetourFormulaire"));
        getServletContext().getRequestDispatcher((String)params.get("urlReponse")).forward(request,
                response);
        return;
    }
  • 第 5-6 行:从客户端请求中获取参数“txtNom”和“txtAge”的值。
  • 第8-10行:将这些值存储在会话中,以便当用户点击视图[réponse]和[erreurs]中的链接[Retour au formulaire]时能够检索到它们。
  • 第12-19行:验证两个参数值的有效性
  • 第21-28行:如果其中一个参数有误,则显示视图[erreurs(erreurs,lienRetourFormulaire)]。回顾该视图的模板:

<%
// 从模型中获取数据
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs"); 
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>
  • 第30-34行:如果获取的两个参数“txtNom”和“txtAge”具有有效值,则显示视图[reponse(nom,age,lienRetourFormulaire)]。 需注意视图 [reponse] 的模板:

<%
    // 从模型中检索数据
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
  String lienRetourFormulaire=(String)request.getAttribute("lienRetourFormulaire");
%>

6.6.6. 方法 [doRetourFormulaire]

该方法处理第 3 号请求 [GET /personne2/main?action=retourFormulaire]。其代码如下:

// 显示预填充表单
    void doRetourFormulaire(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // 获取用户会话
        HttpSession session = request.getSession(true);        
        // 准备表单模板
        // 会话中是否包含姓名?
        String nom = (String) session.getAttribute("nom");
        if (nom == null)
            session.setAttribute("nom", "");
        // 会话中是否包含年龄?
        String age = (String) session.getAttribute("age");
        if (age == null)
            session.setAttribute("age", "");
        // urlAction
        request.setAttribute("urlAction", (String)params.get("urlControleur"));
        // 显示表单
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }

该方法执行完毕后,应显示视图 [formulaire],其中已预先填入用户最近的输入内容。回顾视图 [formulaire] 的模板:


<%
    // 从模型中获取数据
  String nom=(String)session.getAttribute("nom");
  String age=(String)session.getAttribute("age");
  String urlAction=(String)request.getAttribute("urlAction");
%>

因此,方法 [doRetourFormulaire] 必须构建上述视图模型。

  • 第 4 行:获取控制器中存储了用户输入值(姓名、年龄)的会话。
  • 第 7 行:从会话中获取姓名
  • 第8-9行:若未存在,则将其作为空值存入。在应用程序的正常运行中,这种情况不应发生,因为操作[retourFormulaire]总是发生在操作[validationFormulaire]之后,即在输入数据存入会话之后。 但会话可能因有效期有限(通常为几十分钟)而过期。此时,第4行会创建一个新会话,该会话中不会包含用户名。因此,需在新会话中设置一个空用户名。
  • 第11-13行:年龄字段也采用相同处理方式
  • 若忽略会话过期的问题,则第3-13行是多余的。表单中的[nom,age]字段已存在于会话中,因此无需再次将其放入会话。
  • 第15行:设定模板中[urlAction]元素的值

6.7. 测试

启动或重启 Tomcat。访问 URL [http://localhost:8080/personne2],然后按照第 6.1 节中的示例继续进行测试。