Skip to content

5. MVC Web 应用程序 [person] – 版本 1

5.1. 应用程序视图

该应用程序使用了前几个示例中的表单。应用程序的首页如下所示:

Image

我们将此视图称为 [form] 视图。如果输入内容正确,它们将显示在一个名为 [response] 的视图中:

Image

如果输入有误,错误信息将显示在一个名为 [errors] 的视图中:

Image

5.2. 应用程序架构

[person1] Web 应用程序将采用以下架构:

Image

这是一种单层架构:没有 [业务] 或 [DAO] 层,只有 [Web] 层。[ServletPersonne] 是应用程序控制器,负责处理所有客户端请求。为了响应这些请求,它会使用三个视图 [form、response、errors] 中的一个。

我们需要确定 [ServletPersonne] 控制器在收到用户请求时,如何决定应采取的操作。客户端请求是一个 HTTP 流,其内容会因使用 GET 还是 POST 命令而有所不同。

GET 请求

在此情况下,HTTP 数据流如下所示:

GET /URL HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: fr-fr,fr;q=0.8,en;q=0.6,en-us;q=0.4,de;q=0.2
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
[ligne vide]

第 1 行指定请求的 URL,例如:

GET /personne1/ HTTP/1.1

该 URL 可用于指定要执行的操作。可以使用多种方法:

  1. URL 参数用于指定操作,例如 [/app?action=add&id=4]。在此,[action] 参数告知控制器当前请求的是哪个操作。
  2. URL 的最后一个元素指定操作,例如 [/app/add?id=4]。在此,URL 的最后一个元素 [/add] 由控制器用于确定其必须执行的操作。

还有其他解决方案。上述两种是常见的。

POST 请求

在这种情况下,HTTP 流程如下所示:

POST /URL HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: fr-fr,fr;q=0.8,en;q=0.6,en-us;q=0.4,de;q=0.2
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8080/personne1/main
Cookie: JSESSIONID=9F5E5BFA29643FC6B1601EEED907E1F9
Content-Type: application/x-www-form-urlencoded
Content-Length: 43
[ligne vide]
txtNom=&txtAge=&action=validationFormulaire

第 1 行指定了请求的 URL,例如:

POST /personne1/main HTTP/1.1

该 URL 可用于指定要执行的操作,这与 GET 请求类似。在 GET 请求中,[action] 参数包含在 URL 中。此处同样适用,例如:

POST /appli?action=ajouter&id=4 HTTP/1.1

不过,[action] 参数也可以包含在提交的参数中(如上文第 15 行所示),例如:

POST /appli HTTP/1.1
...
[ligne vide]
action=ajouter&id=4

接下来,我们将使用这些不同的技术来告诉控制器该做什么:

  • 在请求的 URL 中包含 action 参数:
POST /appli?action=ajouter&id=4 HTTP/1.1
  • 提交 action 参数:
POST /appli HTTP/1.1
...
[ligne vide]
action=ajouter&id=4
  • 将 URL 的最后一个元素用作操作名称:
POST /appli/ajouter?id=4 HTTP/1.1

5.3. Eclipse 项目

要为 Web 应用程序 [personne1] 创建 Eclipse 项目 [mvc-personne-01],请按照第 3.1 节中的步骤操作。

Image

我们将不保留默认上下文 [mvc-personne-01]。我们将如下所示选择 [personne1]:

Image

结果如下:

Image

若需更改 Web 应用程序上下文,请使用以下选项:[右键单击项目 -> 属性 -> J2EE]:

Image

在 [1] 中输入新的上下文。

我们将在 [WEB-INF] 文件夹中创建一个名为 [vues] 的子文件夹:[右键单击 WEB-INF -> 新建 -> 文件夹]:

新项目现在如下所示:

Image

项目完成后将呈现如下效果:

Image

  • [ServletPersonne] 控制器位于 [src] 文件夹中
  • 用于视图 [form、response、errors] 的 JSP 页面位于 [WEB-INF/vues] 文件夹中,这阻止了用户直接访问它们,如下例所示:

Image

接下来我们将介绍 [/personne1] Web 应用程序的各个组件。建议读者在阅读过程中逐步创建这些组件。

5.4. [person1] Web 应用程序的配置

/person1 应用程序的 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-01</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>
    </servlet>
    <!--  Mapping ServletPersonne-->
    <servlet-mapping>
        <servlet-name>personne</servlet-name>
        <url-pattern>/main</url-pattern>
    </servlet-mapping>
    <!--  welcome files -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>
 

这个配置文件说明了什么?

  • 第 34–37 行:URL /main 由名为 person 的 Servlet 处理
  • 第 10–13 行:名为“person”的 Servlet 是 [ServletPersonne] 类的实例
  • 第 14–19 行:定义了一个名为 [urlResponse] 的配置参数。这是 [response] 视图的 URL。
  • 第 20–25 行:定义了一个名为 [urlErrors] 的配置参数。这是 [errors] 视图的 URL。
  • 第 26–31 行:定义名为 [urlForm] 的配置参数。这是 [form] 视图的 URL。
  • 第 40 行:[index.jsp] 将作为应用程序的首页。

用于 [form、response、errors] 视图的 JSP 页面的 URL 均由配置参数定义。这样,在移动这些页面时无需重新编译应用程序。

当用户请求 URL [/person1] 时,[index.jsp] 文件将发送响应(主页文件,第 40 行)。该文件位于 [WebContent] 文件夹的根目录下:

Image

其内容如下:


<%@ 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("/personne1/main");
%>

[index.jsp] 页面仅将客户端重定向至 URL [/person1/main]。因此,当浏览器请求 URL [/person1] 时,[index.jsp] 会向其发送以下 HTTP 响应:

1
2
3
4
5
6
7
HTTP/1.x 302 Déplacé Temporairement
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=9F5E5BFA29643FC6B1601EEED907E1F9; Path=/personne1
Location: http://localhost:8080/personne1/main
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 0
Date: Thu, 18 May 2006 15:45:23 GMT
  • 第 1 行:HTTP/1.1 响应,用于指示服务器重定向到另一个 URL
  • 第 4 行:浏览器应重定向到的 URL

收到此响应后,浏览器将按照指示(第 4 行)请求 URL [/person1/main]。[/person1] 应用程序的 [web.xml] 文件指定此请求将由 [ServletPersonne] 控制器处理(第 35–36 行)。

5.5. 视图代码

我们从创建视图开始编写 Web 应用程序。这些视图有助于定义用户在图形界面方面的需求,并且可以在不依赖控制器的情况下进行测试。

5.5.1. [表单]视图

此视图用于显示用于输入姓名和年龄的表单:

Image

HTML
HTML 类型
名称
角色
1
<input type="text">
txtName
输入姓名
2
<input type="text">
txtAge
输入年龄
3
<input type="submit">
 
将输入的值发送至 URL /person1/main 的服务器
4
<input type="reset">
 
将页面恢复到浏览器最初接收时的状态
5
<input type="button">
 
用于清除输入字段 [1] 和 [2] 的内容

该按钮由 JSP 页面 [formulaire.jsp] 生成。其模板包含以下元素:

  • [name]:在与会话属性键“name”关联的会话属性中找到的名称(字符串)
  • [age]:在与会话属性键“age”关联的数据中找到的年龄(字符串)

当用户请求 URL [/person1/main](即 [ServletPersonne] 控制器的 URL)时,将获取 [form] 视图。生成 [form] 视图的 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">
<%
    // on récupère les données du modèle
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
%>
    
<html>
    <head>
      <title>Personne - formulaire</title>
  </head>
  <body>
      <center>
        <h2>Personne - formulaire</h2>
      <hr>
      <form 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>
 
  • 第6-7行:JSP页面首先从请求中获取其模型的[name, age]元素。在正常应用程序运行中,控制器[ServletPersonne]将构建此模型。
  • 第18-38行:JSP页面生成一个HTML表单(<form>标签)
  • 第 18 行:<form> 标签未指定 action 属性,用于指定处理 [Submit] 按钮(第 32 行)提交的值的 URL。 表单值将被提交至获取表单的 URL,即 [ServletPersonne] 控制器的 URL。因此,该控制器既用于生成最初由 GET 请求请求的空表单,也用于处理通过 [Submit] 按钮提交的输入数据。
  • 提交的值来自 HTML 字段 [txtName](第 22 行)、[txtAge](第 26 行)和 [action](第 37 行)。最后一个参数将使控制器知道它需要做什么。
  • 表单首次显示时,输入字段 [txtName] 和 [txtAge] 分别通过变量 [name](第 22 行)和 [age](第 26 行)进行初始化。 这些变量从请求属性(第 6–7 行)中获取其值,而这些属性已知是由 Servlet 初始化的。因此,是 Servlet 设置了表单输入字段的初始内容。
  • 第 33 行:类型为 [reset] 的 [Reset] 按钮将表单恢复到浏览器接收它时的状态。
  • 第 34 行:类型为 [reset] 的 [Clear] 按钮目前没有功能。

今后,当需要同时指定视图名称及其模型时,我们将把此视图称为 [form(name, age)] 视图。此外,请注意,当用户点击 [Submit] 按钮时,[txtName, txtAge] 参数会被提交到 URL [/person1/main]。

5.5.2. [response]视图

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

Image

该视图由 JSP 页面 [response.jsp] 生成。其模板包含以下元素:

  • [name]:在会话属性中找到的名称(字符串),与键“name”相关联
  • [age]:一个年龄(字符串),该值将出现在会话属性中,与键“age”相关联

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">
<%
    // on récupère les données du modèle
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
%>
 
<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>      
  </body>
</html>
 
  • 第 6-7 行:JSP 页面首先从请求中获取其模型的 [name, age] 元素。在应用程序的正常运行过程中,[ServletPersonne] 控制器将构建此模型。
  • 随后,模型元素 [name, age] 分别在第 20 行和第 24 行显示出来

此后,我们将此视图称为 [response(name, age)] 视图。

5.5.3. [errors]视图

该视图以以下形式报告输入错误:

Image

它由 JSP 页面 [errors.jsp] 生成。其模型由以下元素组成:

  • [errors]:一个错误消息列表(ArrayList),这些消息将出现在请求属性中,并关联键“errors”

JSP 页面 [errors.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" %>
 
<%
// on récupère les données du modèle
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");  
%>
 
<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");
        }//for
      %>
    </ul>
  </body>
</html>
 
  • 第 8 行:JSP 页面首先从请求中的模型中检索 [errors] 元素。该元素表示一个包含 String 元素的 ArrayList 对象。这些元素即为错误消息。在应用程序正常运行期间,[ServletPersonne] 控制器将构建此模型。
  • 第 18–22 行:显示错误消息列表。为此,我们需要在页面的 HTML 主体中编写 Java 代码。我们应始终尽量减少此类代码,以避免 HTML 代码杂乱无章。稍后我们将看到,有方法可以减少 JSP 页面中的 Java 代码量。
  • 第 4 行:请注意 JSP 页面所需包的 import 标签

我们将此视图称为 [errors(errors)] 视图。

5.6. 视图测试

即使尚未编写控制器,也可以测试 JSP 页面的有效性。为此,必须满足两个条件:

  • 必须能够直接从应用程序请求这些页面,而无需通过控制器
  • JSP 页面必须自行初始化模型,该模型通常由控制器构建

要执行这些测试,我们将视图的 JSP 页面复制到 Eclipse 项目的 [/WebContent/JSP] 文件夹中:

Image

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

[form.jsp]:


...
 
<%
  // -- test : on crée le modèle de la page
  request.setAttribute("nom","tintin");
  request.setAttribute("age","30");
%>
 
<%
    // on récupère les données du modèle
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
%>
    
<html>
    <head>
...

第3至7行是新增的,用于生成第11至12行所需的模板。

[reponse.jsp]:


...
 
<%
  // -- test : on crée le modèle de la page
  request.setAttribute("nom","milou");
  request.setAttribute("age","10");
%>
 
<%
    // on récupère les données du modèle
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
%>
 
    
<html>
    <head>
...

第3至7行是添加的,用于创建第11至12行中页面所需的模板。

[errors.jsp]:


...
 
<%
  // -- test : on crée le modèle de la page
  ArrayList<String> erreurs1=new ArrayList<String>();
  erreurs1.add("erreur1");
  erreurs1.add("erreur2");
  request.setAttribute("erreurs",erreurs1);
%>
 
<%
// on récupère les données du modèle
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");  
%>
 
    
<html>
    <head>
...

第3至9行是新增的,用于构建第13行所需的模型。

如果尚未启动 Tomcat,请先启动,然后访问以下 URL:

 

我们得到了预期的视图。既然我们对应用程序的 JSP 页面已有相当程度的信心,就可以继续编写其控制器 [ServletPersonne] 了。

5.7. [ServletPersonne] 控制器

剩下的就是编写 Web 应用程序的核心:控制器。其作用是:

  • 获取客户端的请求,
  • 处理客户端请求的操作,
  • 返回相应的视图。

[ServletPersonne] 控制器将处理以下操作:

请求
请求
origin
处理
1
[GET /person1/hand]
用户输入的 URL
- 发送空的 [form] 视图
2
[POST /person1/hand]
并携带参数 [txtName,
txtAge, action]
点击
[提交] 按钮
[表单]
- 检查参数 [txtName, txtAge] 的值
- 如果答案错误,则返回视图 [errors(errors)]
- 如果正确,则返回 [response(name,age)] 视图

当用户请求 URL [/person1/main] 时,应用程序便会启动。根据应用程序的 [web.xml] 文件(参见第 5.4 节),该请求由 ServletPersonne 类的实例处理,下面我们将对此进行说明。

5.7.1. 控制器框架

[ServletPersonne] 控制器的代码如下:

package istia.st.servlets.personne;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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

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

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

    // initial view display
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
...
    }

    // form validation
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
...
    }

    // post
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        // we hand over to GET
        doGet(request, response);
    }
}
  • 第 20–22 行:Servlet 首次加载时执行的 [init] 方法
  • 第 25–28 行:当向应用程序发出 GET 请求时,Web 服务器调用的 [doGet] 方法
  • 第 42–46 行:当向应用程序发出 POST 请求时,Web 服务器调用的 [doPost] 方法。如图所示,这也将由 [doGet] 方法(第 45 行)处理。
  • 第 31–33 行:[doInit] 方法处理操作 #1 [GET /person1/main]
  • 第 36–39 行:[doValidationFormulaire] 方法处理操作 #2 [POST /person1/main],并处理提交的参数 [txtName, txtAge, action]。

接下来我们将详细介绍控制器中的各种方法

5.7.2. 控制器初始化

当 Servlet 容器加载控制器类时,会执行其 [init] 方法。此过程仅发生一次。一旦加载到内存中,控制器便驻留于此并处理来自不同客户端的请求。每个客户端由独立的执行线程处理,因此控制器的方法会由不同线程同时执行。请注意,正因如此,控制器绝不能包含其方法可能修改的字段。其字段必须为只读。 这些字段由 [init] 方法初始化,这也是该方法的主要作用。该方法具有一个独特特征:仅由单个线程执行一次。因此,在此方法内部,对控制器字段的并发访问不会产生问题。[init] 方法的目的是初始化 Web 应用程序所需的对象,这些对象将以只读模式被所有客户端线程共享。这些共享对象可以放置在两个位置:

  • 控制器的私有字段
  • 应用程序的执行上下文(ServletContext)

[ServletPersonne] 控制器 [init] 方法的代码如下:

package istia.st.servlets.personne;

...
@SuppressWarnings("serial")
public class ServletPersonne extends HttpServlet {
    // instance parameters
    private String urlErreurs = null;
    private ArrayList erreursInitialisation = new ArrayList<String>();
    private String[] paramètres={"urlFormulaire","urlReponse"};
    private Map params=new HashMap<String,String>();

    // init
    @SuppressWarnings("unchecked")
    public void init() throws ServletException {
        // retrieve servlet initialization parameters
        ServletConfig config = getServletConfig();
        // other initialization parameters are processed
        String valeur=null;
        for(int i=0;i<paramètres.length;i++){
            // parameter value
            valeur=config.getInitParameter(paramètres[i]);
            // present parameter?
            if(valeur==null){
                // we note the error
                erreursInitialisation.add("Le paramètre ["+paramètres[i]+"] n'a pas été initialisé");
            }else{
                // parameter value is stored
                params.put(paramètres[i],valeur);
            }
            // the [errors] view url has a special treatment
            urlErreurs = config.getInitParameter("urlErreurs");
            if (urlErreurs == null)
                throw new ServletException(
                        "Le paramètre [urlErreurs] n'a pas été initialisé");
        }
    }
...
}
  • 第 16 行:获取 Web 应用程序配置,即 [web.xml] 文件的内容
  • 第 19–29 行:获取 Servlet 初始化参数,其名称在第 9 行的 [parameters] 数组中定义
  • 第 21 行:获取参数值
  • 第 25 行:如果参数不存在,则将该错误添加到最初为空的 [initializationErrors] 错误列表中(第 8 行)。
  • 第 28 行:如果参数存在,则将其与值一起存储在初始为空的 [params] 字典中(第 10 行)。
  • 第 31–35 行:[urlErrors] 参数必须存在,因为它指定了能够显示任何初始化错误的 [errors] 视图的 URL。如果该参数不存在,则通过抛出 [ServletException] 来终止应用程序(第 33 行)。

5.7.3. [doGet] 方法

[doGet] 方法处理发送到 Servlet 的 GET 和 POST 请求,因为 [doPost] 方法会重定向到 [doGet] 方法。其代码如下:

package istia.st.servlets.personne;

...
@SuppressWarnings("serial")
public class ServletPersonne extends HttpServlet {
    // instance parameters
    private String urlErreurs = null;
    private ArrayList erreursInitialisation = new ArrayList<String>();
    private String[] paramètres={"urlFormulaire","urlReponse"};
    private Map params=new HashMap<String,String>();

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

        // check how the servlet was initialized
        if (erreursInitialisation.size() != 0) {
            // we hand over to the error page
            request.setAttribute("erreurs", erreursInitialisation);
            getServletContext().getRequestDispatcher(urlErreurs).forward(
                    request, response);
            // end
            return;
        }

        // retrieve the request sending method
        String méthode=request.getMethod().toLowerCase();
        // retrieve the action to be executed
        String action=request.getParameter("action");
        // action?
        if(action==null){
            action="init";
        }
        // execution action
        if(méthode.equals("get") && action.equals("init")){
            // start application
            doInit(request,response);
            return;
        }
        if(méthode.equals("post") && action.equals("validationFormulaire")){
            // validation of input form
            doValidationFormulaire(request,response);
            return;
        }
        // other cases
        doInit(request,response);
    }

    // post
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        // we hand over to GET
        doGet(request, response);
    }
}
  • 第 18–25 行:我们检查初始化错误列表是否为空。如果不是,则显示 [errors(initializationErrors)] 视图,该视图将报告错误。

要理解这段代码,你需要回顾 [errors] 视图模板:

<%
// on récupère les données du modèle
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");  
%>

[errors] 视图期望请求中存在名为“errors”的键。控制器在第 20 行创建了该键。

  • 第 28 行:我们获取客户端用于发起请求的 [get] 或 [post] 方法
  • 第 30 行:从请求中获取 [action] 参数的值。请注意,在我们的应用程序中,只有请求 #2 [POST /person1/main] 包含 [action] 参数。在此请求中,该参数的值为 [validationFormulaire]。
  • 第 31–34 行:如果 [action] 参数不存在,则将其值设为 "init"。初始请求 #1 [GET /person1/main] 即属于这种情况。
  • 第 36–40 行:处理请求 #1 [GET /person1/main]。
  • 第 41–45 行:处理请求 #2 [POST /person1/main]。
  • 第 47 行:如果前两种情况均不适用,则按情况 #1 进行处理

5.7.4. [doInit] 方法

该方法处理请求 #1 [GET /person1/main]。对于此请求,它必须返回空的 [form(name, age)] 视图。其代码如下:

package istia.st.servlets.personne;
...

@SuppressWarnings("serial")
public class ServletPersonne extends HttpServlet {
    // instance parameters
    private String urlErreurs = null;
    private ArrayList erreursInitialisation = new ArrayList<String>();
    private String[] paramètres={"urlFormulaire","urlReponse"};
    private Map params=new HashMap<String,String>();

...    
    // initial view display
    void doInit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        // send the empty form
        request.setAttribute("nom", "");
        request.setAttribute("age", "");
        getServletContext().getRequestDispatcher((String)params.get("urlFormulaire")).forward(
                request, response);
        return;
    }
...
}
  • 第 18-19 行:显示 [form] 视图。回顾一下该视图所期望的模型:

<%
    // on récupère les données du modèle
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
%>
  • 第 16-17 行:[form] 视图的 [name, age] 模型被初始化为空字符串。

5.7.5. [doValidationForm] 方法

该方法处理请求 #2 [POST /person1/main],其中提交的参数为 [action, txtName, txtAge]。其代码如下:

package istia.st.servlets.personne;

...
@SuppressWarnings("serial")
public class ServletPersonne extends HttpServlet {
    // instance parameters
    private String urlErreurs = null;
    private ArrayList erreursInitialisation = new ArrayList<String>();
    private String[] paramètres={"urlFormulaire","urlReponse"};
    private Map params=new HashMap<String,String>();
...
    // form validation
    void doValidationFormulaire(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException{
        // parameters are retrieved
        String nom = request.getParameter("txtNom");
        String age = request.getParameter("txtAge");
        // parameter verification
        ArrayList<String> erreursAppel = new ArrayList<String>();
        // name must be non-empty
        nom = nom.trim();
        if (nom.equals(""))
            erreursAppel.add("Le champ [nom] n'a pas été rempli");
        // age must be an integer >=0
        if (!age.matches("^\\s*\\d+\\s*$"))
            erreursAppel.add("Le champ [age] est erroné");
        // errors in the parameters?
        if (erreursAppel.size() != 0) {
            // send error page
            request.setAttribute("erreurs", erreursAppel);
            getServletContext().getRequestDispatcher(urlErreurs).forward(request, response);
            return;
        }
        // parameters are correct - send response page
        request.setAttribute("nom", nom);
        request.setAttribute("age", age);
        getServletContext().getRequestDispatcher((String)params.get("urlReponse")).forward(request,
                response);
        return;
    }
...
}
  • 第 16-17 行:从客户端请求中获取 "txtNom" 和 "txtAge" 参数的值。
  • 第 19-26 行:检查这两个参数的有效性
  • 第 28-33 行:如果任一参数不正确,则显示 [errors(callErrors)] 视图。回顾该视图的模板:

<%
// on récupère les données du modèle
  ArrayList erreurs=(ArrayList)request.getAttribute("erreurs");  
%>
  • 第 35-38 行:如果检索到的两个参数“txtNom”和“txtAge”具有有效值,则显示 [response(name, age)] 视图。回顾 [response] 视图的模板:

<%
    // on récupère les données du modèle
  String nom=(String)request.getAttribute("nom");
  String age=(String)request.getAttribute("age");
%>
 

5.8. 测试

让我们按照第 3.3 节所述的步骤,将 [mvc-personne-01] 项目添加到 Tomcat 应用程序中:

Image

启动 Tomcat。完成后,我们可以继续进行第 5.1 节中作为示例展示的测试。我们还可以添加更多测试。例如,我们可以从 web.xml 中移除一个 urlXXX 配置参数,并观察结果。如下所示,[web.xml] 中已将其中一个参数注释掉:


        <!-- 
            <init-param>
              <param-name>urlFormulaire</param-name>
              <param-value>
                /WEB-INF/vues/formulaire.jsp
              </param-value>
            </init-param>
        -->

我们启动或重启 Tomcat,并请求 URL [http://localhost:8080/personne1/main]。我们得到以下响应:

Image