Skip to content

2. Web 编程基础

本章的主要目的是介绍Web编程的基本原理,这些原理与具体实现所采用的技术无关。书中提供了大量示例,建议读者亲自测试,以便逐步“领悟”Web开发的理念。已具备这些知识的读者可直接跳至第3章

Web 应用程序的组成部分如下:

Image

编号
作用
常见示例
1
OS 服务器
Unix、Linux、Windows
2
Web 服务器
Apache(Unix、Linux、Windows)
IIS (Windows + .NET 平台)
Node.js(Unix、Linux、Windows)
3
在服务器端执行的代码。它们可以由服务器模块或服务器外部的程序执行(CGI)。
JAVASCRIPT (Node.js)
PHP (Apache, IIS)
JAVA(Tomcat、Websphere、JBoss、Weblogic 等)
C#, VB.NET (IIS)
4
数据库——该数据库可以位于运行程序的同一台机器上,也可以通过互联网位于另一台机器上。
Oracle (Linux, Windows)
MySQL (Linux, Windows)
Postgres (Linux, Windows)
SQL Server (Windows)
5
OS 客户端
Unix、Linux、Windows
6
网页浏览器
Chrome、Internet Explorer、Firefox、Opera、Safari 等
7
在浏览器中执行的客户端脚本。这些脚本无法访问客户端的磁盘
JavaScript(所有浏览器)

2.1. 基于表单的Web应用程序中的数据交换

Image

编号
角色
1
浏览器首次请求 URL,请求 (http://machine/url)。未传递任何参数。
2
Web服务器向其发送了该URL的网页。 该页面可以是静态的,也可以由服务器脚本(SA)动态生成,该脚本可能使用了数据库中的内容(SB、SC)。 在此,脚本将检测到 URL 被请求时未传递参数,并生成初始网页。
浏览器接收该页面并显示(CA)。浏览器端的脚本(CB)可能已修改了服务器发送的初始页面。 随后,通过用户(CD)与脚本(CB)之间的交互,网页将被修改。特别是表单将被填写。
3
用户提交表单数据,这些数据随后将被发送至Web服务器。 浏览器将重新请求初始的URL页面(或根据具体情况请求其他页面),并同时将表单值传输给服务器。为此,它可以使用两种方法,即GET和POST。 收到客户端请求后,服务器将触发与所请求的URL关联的脚本(SA),该脚本将检测参数并进行处理。
4
服务器返回由程序生成的网页(SA、SB、SC)。此步骤与前面的步骤2完全相同。后续交互将按照步骤2和步骤3进行。

2.2. 静态网页、动态网页

静态网页由文件 HTML 表示。动态网页是由 Web 服务器“即时”生成的 HTML 页面。

2.2.1. 静态页面 HTML(HyperText 标记语言)

让我们构建第一个 Spring 项目 MVC [1-2]:

  • 在 [1-2] 中,我们创建了一个基于 Spring Boot 的新项目 [http://projects.spring.io/spring-boot/];
  • [3-7] 中的信息用于项目的 Maven 配置;
  • 在 [3] 中,是 Maven 项目的名称;
  • 在 [4] 中,是项目编译结果将被放置的 Maven 包;
  • 在 [5] 中,是编译产物的名称;
  • [6] 表示项目描述;
  • [7],项目可执行类所在的包;
  • 在 [8] 中,指定了项目的类型。这是一个使用 Thymeleaf 视图的 Web 项目。这里可以看到 Spring Boot 项目提供的所有现成 Maven 依赖项;
  • 在 [9] 中,指定由 Maven 构建生成的产物将打包为 jar 归档文件而非 war 文件。该项目将使用其依赖项中包含的嵌入式 Tomcat 服务器;
  • 在 [10] 中,我们继续进行向导操作;
  • 在 [11] 中,指定项目文件夹;
  • 在 [12] 中,完成向导;
  • 在 [13] 中,生成的项目。

让我们查看生成的文件 [pom.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>istia.st.springmvc</groupId>
    <artifactId>intro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springmvc-intro</name>
    <description>Les bases de la programmation web</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
        <relativePath /> <!-- 从存储库中查找父级 -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>istia.st.springmvc.Application</start-class>
        <java.version>1.7</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

该文件包含了向导中输入的所有信息。在第26至30行,我们发现了一个此前未知的依赖项。它支持将单元测试JUnit与Spring集成。

首先,让我们在此项目中创建一个静态页面 HTML。该页面默认应放置在 [src / main / resources / static] 文件夹中:

  • 在 [1-4] 中,我们在 [static] 文件夹中创建一个名为 HTML 的文件;
  • 在 [6] 中,为页面命名;
  • 生成 [7],页面已添加。

创建的页面内容如下:


<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>

</body>
</html>
  • 第2-10行:代码由根标签<html>限定;
  • 第3-6行:<head>标签界定了所谓的页面头部;
  • 第7-9行:标签<body>界定了所谓的页面主体。

我们将该代码修改如下:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>essai 1 : une page statique</title>
</head>
<body>
  <h1>Une page statique...</h1>
</body>
</html>
  • 第 5 行:定义页面标题——将作为显示该页面的浏览器窗口标题显示;
  • 第 8 行:大号文本(<h1>)。

运行应用程序 [1-3]:

然后使用浏览器访问 URL [http://localhost:8080/exemple-01.html]:

  • 在 [1] 中,所查看页面的 URL;
  • [2],即窗口标题——由页面中的 <title> 标签提供;
  • [3],即页面正文——由<h1>标签提供。

让我们看看 [4-5],这是浏览器接收到的 HTML 代码:

  • 在 [5] 中,浏览器接收到了我们构建的页面 HTML。它解析了该页面并将其转换为图形显示。

2.2.2. 一个动态的 Thymeleaf 页面

现在我们来创建一个 Thymeleaf 页面。这是一个典型的 HTML 页面,其中标签被 [Thymeleaf] 和 [http://www.thymeleaf.org/] 属性所增强。 操作步骤与创建 HTML 页面类似,但这次需要将新页面 HTML 放置在 [templates] 文件夹中:

 

页面 [exemple-02.html] 将显示如下:


<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>spring mvc intro</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Il est ' + ${heure}">Voici l'heure</p>
</body>
</html>
  • 第 8 行:标签 <p> 是一个 HTML 标签,用于在显示的页面中插入一个段落。 [th:text] 是一个 [Thymeleaf] 属性,其作用取决于 [Thymeleaf] 是否生效:
    • 如果 [Thymeleaf] 不解析页面 HTML,则属性 [th:text] 将被忽略,因为 HTML 中不认识该属性。 此时显示的文本将是 [Voici l'heure],
    • 如果 [Thymeleaf] 能解析页面 HTML,则 [th:text] 属性将被评估,其值将替换文本 [Voici l'heure]。 其值将类似于 [Il est 17:11:06];

让我们看看实际效果。我们将页面 [templates / exemple-02.html] 复制到文件夹 [static] 中。放置在此文件夹中的页面 HTML 不会被 [Thymeleaf] 解析:

我们像之前多次做过的那样运行应用程序,然后通过浏览器请求 URL 和 [http://localhost:8080/exemple-02.html]:

我们在 [1] 中看到,[th:text] 属性未被解析,但也未引发任何错误。[2] 中接收到的页面源代码表明,浏览器确实完整接收了该页面。

让我们回到文件夹 [templates] 中的页面 [exemple-02.html]:

  

位于文件夹 [templates] 中的 HTML 页面由 [Thymeleaf] 进行解析。让我们回到该页面的代码:


<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>spring mvc intro</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Il est ' + ${heure}">Voici l'heure</p>
</body>
</html>
  • 第 7 行:[Thymeleaf] 将解析 [th:text] 属性,并将 [Voici l'heure] 替换为该表达式的值:
"'Il est ' + ${heure}"

该表达式使用了变量 [${heure}],其中 [heure] 属于视图 [exemple-02.html] 的模型。因此我们需要创建该模型。为此,我们将参考第 1.6 节中的示例。我们按以下方式更新项目:

在 [1] 中,我们添加以下控制器:


package istia.st.springmvc;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController {

    @RequestMapping("/")
    public String heure(Model model) {
        // 时间格式
        SimpleDateFormat formater = new SimpleDateFormat("HH:MM:ss");
        // 当前时间
        String heure = formater.format(new Date());
        // 将时间放入视图模板
        model.addAttribute("heure", heure);
        // 显示视图[exemple-02.html]
        return "exemple-02";
    }
}
  • 第13-14行:方法[heure]处理URL和[/];
  • 第 14 行:[Model model] 是一个空模板。操作 [heure] 必须向其中添加其希望在模板中显示的属性。 已知视图 [exemple-02.html] 期待一个名为 [heure] 的属性;
  • 第 19-22 行:实现了上述说明。视图 [exemple-02.html] 将被显示(第 22 行),其模板中包含一个名为 [heure] 的属性(第 20 行);
  • 第 16 行:创建一个日期格式化器。所使用的格式 [HH:MM:ss] 是一种 [heures:minutes:secondes] 格式,其中小时位于 [0-24] 区间内;
  • 第18行:使用该格式化器对当日日期进行格式化;
  • 第20行:将生成的时间与名为[heure]的属性关联;

我们启动应用程序并请求 URL [/]:

  • 在 [1] 中获取页面,在 [2] 中获取其内容 HTML。可以发现初始文本 [Voici l'heure] 已完全消失;

如果现在刷新页面 [1](F5),我们会看到另一种显示(新时间),而 URL 则保持不变。 这就是页面的动态特性:其内容会随时间推移而变化。

综上所述,动态页面与静态页面在性质上存在根本差异。

2.2.3. Spring Boot 应用程序配置

让我们回到 Eclipse 项目的架构:

文件 [application.properties] 用于配置 Spring Boot 应用程序。目前该文件为空。我们可以利用它以多种方式配置应用程序,具体方法详见 URL 和 [http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html]。 我们将参考 [2] 文件来使用 [application.properties] 文件:

  • 第 1 行:设置 Web 应用程序的服务端口;
  • 第 2 行:设置 Web 应用程序的上下文;

通过此配置,将生成静态页面 [exemple-01.html],其中包含 URL 和 [http://localhost:9000/intro/exemple-01.html]:

 

2.3. 浏览器端脚本

HTML 页面可以包含由浏览器执行的脚本。目前(2015年1月),主要的浏览器端脚本语言是 JavaScript。已有数百个基于该语言构建的库,旨在为开发人员提供便利。

让我们在现有项目的 [static] 文件夹中创建一个新的 [exemple-03.html] 页面:

  

编辑文件 [exemple-03.html],内容如下:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>exemple Javascript</title>
  <script type="text/javascript">
    function réagir() {
      alert("Vous avez cliqué sur le bouton !");
    }
  </script>
</head>
<body>
  <input type="button" value="Cliquez-moi" onclick="réagir()" />
</body>
</html>
  • 第 13 行:定义了一个按钮(type 属性),文本为“点击我”(value 属性)。点击该按钮时,将执行 JavaScript 函数 [réagir](onclick 属性);
  • 第 6-10 行:一段 JavaScript 脚本;
  • 第 7-9 行:函数 [réagir];
  • 第 8 行:显示一个包含消息 [Vous avez cliqué sur le bouton] 的对话框。

在浏览器中查看页面:

  • 在 [1] 中,显示的页面;
  • [2],点击按钮时显示的对话框。

点击按钮时,不会与服务器进行交互。JavaScript 代码由浏览器执行。

随着大量JavaScript库的出现,现在可以在浏览器中嵌入真正的应用程序。因此,架构正趋向于以下形式:

  • 1-2:HTML 服务器是一个静态页面服务器 HTML5 / CSS / JavaScript;
  • 3-4:生成的页面 HTML5 / CSS / Javascript 会直接与数据服务器交互。该服务器仅提供不含版式数据的数据 HTML。 由 JavaScript 负责将这些数据插入到浏览器中已存在的 HTML 页面中。

在此架构中,JavaScript 代码可能变得臃肿。因此,我们试图像处理服务器端代码那样将其分层结构化:

  • [UI]层负责与用户交互;
  • [DAO]层负责与数据服务器交互;
  • [métier]层则包含既不与用户交互也不与数据服务器交互的业务流程。该层可能不存在。

2.4. 客户端与服务器的交互

让我们回到最初展示Web应用程序参与者的示意图:

Image

这里我们关注客户端机器与服务器端机器之间的交互。这些交互通过网络进行,有必要回顾一下两台远程机器之间交互的一般结构。

2.4.1. OSI模型

由国际标准化组织(ISO)定义的开放系统互连参考模型(OSI)描述了一个理想网络,其中机器间的通信可通过七层模型来表示:

每一层从下层接收服务,并向上层提供服务。假设位于不同机器A和B上的两个应用程序想要通信:它们在Application层进行通信。 它们无需了解网络运行的所有细节:每个应用程序将想要传输的信息交给下层——即 Présentation 层。因此,应用程序只需了解与 Présentation 层交互的规则即可。 一旦信息进入Présentation层,它就会根据其他规则传递到Session层,以此类推,直到信息到达物理介质并被物理传输到目标机器。 在那里,它将经历与在发送方机器上所经历的相反的处理过程。

在每一层,负责发送信息的发送方进程将其发送给另一台机器上属于同一层的接收方进程。这一过程遵循某些规则,即所谓的“该层协议”。因此,最终的通信图示如下:

各层的作用如下:

物理层
负责在物理介质上传输比特。 该层包含数据处理终端设备(E.T.T.D.),如终端或计算机,以及数据电路终端设备(E.T.C.D.),如调制解调器、多路复用器和集中器。该层的主要关注点包括:
  • 信息编码方式的选择(模拟或数字)
  • 传输模式的选择(同步或异步)。
数据链路层
隐藏物理层的物理特性。检测并纠正传输错误。
网络
管理发送至网络的信息应遵循的路径。这被称为路由:确定信息应遵循的路线,以便其到达接收方。
传输
允许两个应用程序之间进行通信,而前几层仅支持机器间的通信。该层提供的一项服务可能是多路复用:传输层可利用同一条网络连接(机器间连接)来传输属于多个应用程序的信息。
会话
该层包含使应用程序能够在远程机器上建立并维持工作会话的服务。
表示层
该层旨在统一不同机器上的数据表示形式。因此,来自机器A的数据将在发送至网络前,由机器A的Présentation层按照标准格式进行“包装”。 数据到达目标机器B的Présentation层后,该层将通过标准格式识别这些数据,并以另一种方式进行包装,以便机器B上的应用程序能够识别它们。
应用程序
在此层级,通常存在与用户密切相关的应用程序,例如电子邮件或文件传输。

2.4.2. TCP/IP 模型

OSI 模型是一个理想的模型。TCP/IP 协议套件以如下形式接近该模型:

  • 网络接口(计算机的网卡)承担 OSI 模型中第 1 层和第 2 层的功能
  • IP层(互联网协议)承担第3层(网络层)的功能
  • TCP(传输控制协议)或UDP(用户数据报协议)层负责实现第4层(传输层)的功能。 TCP协议确保机器间交换的数据包能够顺利到达目的地。若未能送达,则会将丢失的数据包重新发送。 UDP协议不承担此项工作,因此需要由应用程序开发人员来处理。正因如此,在并非100%可靠的互联网环境中,TCP协议才是最常用的。 因此,我们称之为 TCP-IP 网络。
  • 应用层涵盖了 OSI 模型中第 5 至第 7 层的功能。

Web 应用程序位于 Application 层,因此基于 TCP-IP 协议。 客户端和服务器端的 Application 层之间交换消息,这些消息由模型的第 1 至第 4 层负责转发至目的地。为了相互理解,两台机器的应用程序层必须“使用”相同的语言或协议。 Web应用程序所使用的协议称为HTTP(HyperText传输协议)。这是一种文本型协议,即c.a.d,机器通过在网络上交换文本行来实现互通。 这些交互是标准化的,即客户端拥有若干消息来向服务器精确表达其需求,而服务器也拥有若干消息向客户端返回响应。这种消息交互的形式如下:

Image

客户端 --> 服务器

当客户端向Web服务器发出请求时,它会发送

  1. 格式为 HTTP 的文本行来表明其需求;
  2. 一行空行;
  3. 可选地,一个文档。

服务器 --> 客户端

当服务器向客户端响应时,它会发送

  1. 格式为 HTTP 的文本行,以说明其发送的内容;
  2. 一行空行;
  3. 可选地发送一个文档。

因此,双向通信的格式完全一致。在两种情况下,都可能发送文档,尽管客户端向服务器发送文档的情况较为罕见。 但 HTTP 协议对此有所规定。这使得,例如,某网络服务提供商的用户能够将各种文档下载到由该提供商托管的个人网站上。交换的文档可以是任意类型的。以浏览器请求包含图片的网页为例:

  1. 浏览器连接到Web服务器并请求所需的页面。所请求的资源通过URL(统一资源定位符)进行唯一标识。浏览器仅发送HTTP头部信息,而不发送任何文档。
  2. 服务器向其响应。它首先发送 HTTP 头部,表明将发送何种类型的响应。如果请求的页面不存在,则可能返回错误。 如果页面存在,服务器将在响应的 HTTP 头部中说明,随后将发送一个 HTML 文档(HyperText 标记语言)。 该文档由一系列符合 HTML 格式的文本行组成。HTML 文本包含标签(标记),这些标签向浏览器提供如何显示文本的指示。
  3. 客户端根据服务器的 HTTP 标头得知,它将收到一个 HTML 文档。 客户端将解析该文档,并可能发现其中包含图片引用。这些图片并不在 HTML 文档中。因此,客户端向同一 Web 服务器发出新请求,以获取所需的第一个图片。该请求与步骤 1 中的请求完全相同,只是请求的资源不同。 服务器将处理此请求,向客户端发送所请求的图片。此次,在响应中,HTTP的头部信息将明确指出所发送的文件是一张图片,而非HTML文档。
  4. 客户端接收所发送的图像。步骤3和4将不断重复,直到客户端(通常是浏览器)获取到所有能够完整显示该页面的文档为止。

2.4.3. HTTP协议

让我们通过示例来了解 HTTP 协议。浏览器和 Web 服务器之间交换什么?

Web服务或HTTP服务是一种TCP-IP服务,通常运行在80端口上。它也可能运行在其他端口上。在这种情况下,客户端浏览器必须在其发出的URL请求中明确指定该端口。 一个 URL 请求的一般格式如下:

协议://服务器/路径/信息

其中

协议
为 Web 服务。浏览器也可作为 FTP、新闻、Telnet 等服务的客户端。
服务器
提供 Web 服务的机器名称
端口
Web服务的端口。若为80,可省略端口号。这是最常见的情况
路径
指向所请求资源的路径
信息
提供给服务器的补充信息,用于明确客户端的请求

当用户请求加载 URL 时,浏览器会做什么?

  1. 它会与 URL 中的 machine[:port] 部分所指定的机器和端口建立 TCP-IP 通信。 建立 TCP-IP 通信,即是在两台机器之间创建一条通信“管道”。一旦该管道建立,两台机器之间交换的所有信息都将通过它传输。 创建这条通道TCP-IP尚不涉及Web协议HTTP。
  2. TCP-IP管道建立后,客户端将向Web服务器发起请求,并通过发送HTTP格式的文本行(即命令)来完成。它将向服务器发送URL中的路径/信息部分
  3. 服务器将通过同一管道以相同方式进行响应
  4. 双方中的一方将决定关闭该通道。这取决于所使用的 HTTP 协议。 在 HTTP 1.0 协议下,服务器会在每次响应后关闭连接。这迫使需要多次请求以获取构成网页的各个文档的客户端,在每次请求时都必须建立新的连接,从而产生额外开销。 在 HTTP/1.1 协议下,客户端可以指示服务器保持连接开放,直到客户端要求关闭为止。因此,客户端可以通过单次连接获取网页的所有文档,并在获取最后一个文档后自行关闭连接。服务器将检测到此关闭操作,并随之关闭连接。

为了了解客户端与Web服务器之间的交互,我们将使用第9.6节中安装的Chrome浏览器扩展程序[Advanced Rest Client]。我们将处于以下情况:

Image

Web服务器可以是任意一种。我们在此旨在了解浏览器与Web服务器之间将发生的交互。此前,我们创建了以下静态页面 HTML:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>essai 1 : une page statique</title>
</head>
<body>
  <h1>Une page statique...</h1>
</body>
</html>

我们在浏览器中查看该页面:

 

可以看到,请求的 URL 实际返回的是:[http://localhost:9000/intro/exemple-01.html]。 因此,Web 服务器的机器是 localhost(=本地机器),端口为 9000。让我们使用 [Advanced Rest Client] 应用程序来请求相同的 URL:

  • 在 [1] 中,启动应用程序(在 Chrome 新标签页的 [Applications] 标签页中);
  • 在 [2] 中,选择 [Request] 选项;
  • 在 [3] 中,指定要查询的服务器:http://localhost:9000;
  • 在 [4] 中,指定请求的 URL:/intro/exemple-01.html;
  • 在 [5] 中,为 URL 添加可能的参数。此处无参数;
  • 在 [6] 中,指定用于该请求的 HTTP 命令,此处为 GET。

由此得到以下查询:

经此准备的请求 [7] 由 [8] 发送至服务器。随后获得的响应如下:

我们在前面提到,客户端与服务器的交互采用以下形式:

Image

  • 在 [1] 中,可以看到浏览器在其请求中发送的 HTTP 头部。它没有文档可发送;
  • 在 [2] 中,可以看到服务器在响应中发送的 HTTP 头部。在 [3] 中,可以看到服务器发送的文档。

在 [3] 中,我们可以识别出我们放置在 Web 服务器上的静态页面 HTML。

让我们来分析浏览器的请求 HTTP:

GET /intro/exemple-01.html HTTP/1.1
Host: localhost:9000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Content-Type: text/plain; charset=utf-8
Accept: */*
Accept-Encoding: gzip, deflate, sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
  • 第 1 行未由应用程序显示;
  • 第 6 行:浏览器通过 [User-Agent] 标头进行身份识别;
  • 第 7 行:浏览器表示其向服务器发送了一个格式为 UTF-8 的纯文本文档(text/plain)。实际上,此处浏览器并未发送任何文档;
  • 第8行:浏览器表示接受任何类型的响应文档;
  • 第9行:浏览器明确了可接受的文档格式;
  • 第10行:浏览器按优先顺序列出了所需的语言。

服务器通过发送以下 HTTP 头部信息予以响应:

1
2
3
4
5
6
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Sat, 29 Nov 2014 07:31:43 GMT
Content-Type: text/html
Content-Length: 255
Date: Sat, 29 Nov 2014 08:20:52 GMT
  • 第 1 行:未被应用程序显示;
  • 第2行:服务器进行身份识别,此处为Apache-Coyote服务器;
  • 第3行:所发送文档的最后修改日期;
  • 第 4 行:服务器发送的文档类型。此处为 HTML 文档;
  • 第5行:发送的HTML文档的字节大小。
  • 第6行:响应的日期和时间;

2.4.4. 结论

我们通过几个示例,了解了Web客户端请求的结构以及Web服务器对其响应的结构。这种交互是通过HTTP协议实现的,该协议是一组由双方交换的文本格式命令。客户端的请求和服务器的响应具有相同的结构,如下所示:

Image

请求资源的两个常用命令是 GET 和 POST。 命令 GET 不附带文档。而命令 POST 则附带文档,该文档通常是一串字符,汇总了表单中输入的所有值。 命令 HEAD 仅用于请求 HTTP 头部信息,且不附带文档。

应客户端请求,服务器将发送结构相同的响应。所请求的资源将通过 [Document] 部分传输,除非客户端的命令为 HEAD,在此情况下仅发送 HTTP 头部。

2.5. HTML语言基础

Web 浏览器可以显示各种文档,最常见的是 HTML 文档(HyperText 标记语言)。该文档是使用 <balise>texte</balise> 形式的标签进行格式化的文本。 因此,文本 <B>important</B> 将把重要内容显示为粗体。还有一些独立标签,例如标签 <hr/>,它会显示一条水平线。我们不会逐一介绍 HTML 文本中可能出现的标签。 市面上有许多WYSIWYG软件,可让您无需编写任何代码HTML即可构建网页。这些工具会自动生成HTML代码,用于实现通过鼠标和预定义控件设计的版面布局。 因此,用户可以(通过鼠标)在页面中插入一个表格,然后查看软件生成的代码,从而了解在网页中定义表格应使用的标签。这并不复杂。 此外,掌握HTML语言是必不可少的,因为动态Web应用程序必须自行生成HTML代码并发送给Web客户端。该代码由程序生成,当然必须清楚需要生成什么内容,才能让客户端获得其期望的网页。

总而言之,开始进行Web编程时,完全无需掌握HTML语言的全部内容。 不过,掌握这些知识是必要的,可以通过使用WYSIWYG网页构建软件(如DreamWeaver以及其他数十种同类软件)来学习。 探索HTML语言精妙之处的另一种方法是浏览网页,并查看那些具有有趣特性且您尚未了解的网页的源代码。

2.5.1. 一个示例

请看以下示例,其中展示了网页文档中可能出现的一些元素,例如:

  • 一个表格;
  • 一张图片;
  • 一个链接。

一个 HTML 文档通常具有以下形式:

<html>
    <head>
        <title>Un titre</title>
        ...
    </head>
    <body attributs>
        ...
    </body>
</html>

整个文档由 <html>...</html> 标签包围。它由两部分组成:

  1. <head>...</head>:这是文档中不可显示的部分。它向将要显示文档的浏览器提供信息。其中通常包含 <title>...</title> 标签,用于设定浏览器标题栏中显示的文本。此外还可能包含其他标签,特别是定义文档关键词的标签,这些关键词随后会被搜索引擎使用。 该部分还可能包含脚本,通常采用JavaScript或VBScript编写,并将由浏览器执行。
  1. <body 属性>...</body>:这是浏览器将显示的部分。该部分包含的标签会向浏览器指示文档的“预期”视觉呈现形式。每个浏览器都会以自己的方式解释这些标签。 因此,同一网页在不同浏览器中的显示效果可能存在差异。这通常是网页设计师面临的难题之一。

我们示例文档的代码如下:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>balises</title>
</head>

<body style="height: 400px; width: 400px; background-image: url(images/standard.jpg)">
    <h1 style="text-align: center">Les balises HTML</h1>
    <hr />
    <table border="1">
        <thead>
            <tr>
                <th>Colonne 1</th>
                <th>Colonne 2</th>
                <th>Colonne 3</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>cellule(1,1)</td>
                <td style="width: 150px; text-align: center;">cellule(1,2)</td>
                <td>cellule(1,3)</td>
            </tr>
            <tr>
                <td>cellule(2,1)</td>
                <td>cellule(2,2)</td>
                <td>cellule(2,3</td>
            </tr>
        </tbody>
    </table>

    <table>
        <tr>
            <td>Une image</td>
            <td><img border="0" src="images/cerisier.jpg" /></td>
        </tr>
        <tr>
            <td>le site de l'ISTIA</td>
            <td><a href="http://istia.univ-angers.fr">ici</a></td>
        </tr>
    </table>
</body>
</html>
标签
标签与示例 HTML
文档标题
<title>标签</title>(第 5 行)
文本 balises 将显示在浏览器的标题栏中,该浏览器将显示该文档
水平条
<hr/>:显示一条水平线(第 10 行)
表格
<table 属性>....</table>:用于定义表格(第 11、31 行)
<thead>...</thead>:用于定义列标题(第 12、18 行)
<tbody>...</tbody>:用于定义表格内容(第19、30行)
<tr 属性>...</tr>:用于定义一行(第 20、24 行)
<td 属性>...</td>:用于定义一个单元格(第 21 行)
示例
<table border="1">...</table>:border 属性定义表格边框的粗细
<td style="width: 150px; text-align: center;">单元格(1,2)</td>:定义一个单元格,其内容为单元格(1,2)。该内容将水平居中(text-align: center)。该单元格宽度为 150 像素(width: 150px)
图片
<img border="0" src="/images/cerisier.jpg"/> (第36行):定义了一张无边框(border="0")的图片,其源文件位于Web服务器上的/images/cerisier.jpg(src="images/cerisier.jpg")。 该链接位于通过 URL 生成的网页文档中 http://localhost:port/intro/exemple-04.html。 因此,浏览器将请求 URL http://localhost:port/intro/images/cerisier.jpg 以获取此处引用的图片。
链接
<a href="http://istia.univ-angers.fr">此处</a> (第40行):使文本 ici 作为指向 http://istia.univ-angers.fr 的链接。
页面背景
<body style="height:400px;width:400px;background-image:url(images/standard.jpg)"> (第 8 行):表示用作页面背景的图片位于 Web 服务器的 URL [images/standard.jpg] 路径下。 在本示例中,浏览器将请求 http://localhost:port/intro/images/standard.jpg 以获取该背景图片。此外,文档主体将显示在一个高度为 400 像素、宽度为 400 像素的矩形区域内。

从这个简单的例子可以看出,为了生成完整的文档,浏览器必须向服务器发送三次请求:

  1. http://localhost:port/intro/exemple-04.html 以获取文档的源文件 HTML
  2. http://localhost:port/intro/images/cerisier.jpg 用于获取图片 cerisier.jpg
  3. http://localhost:port/intro/images/standard.jpg 获取背景图片 standard.jpg

2.5.2. 一个表单 HTML

以下示例展示了一个表单:

生成此显示效果的代码 HTML 如下:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>formulaire</title>
  <script type="text/javascript">
    function effacer() {
      alert("Vous avez cliqué sur le bouton Effacer");
    }
  </script>
</head>

<body style="height: 400px; width: 400px; background-image: url(images/standard.jpg)">
  <h1 style="text-align: center">Formulaire HTML</h1>
  <form method="post" action="postFormulaire">
    <table>
      <tr>
        <td>Etes-vous marié(e)</td>
        <td>
          <input type="radio" value="Oui" name="R1" />Oui
          <input type="radio" name="R1" value="non" checked="checked" />Non
        </td>
      </tr>
      <tr>
        <td>Cases à cocher</td>
        <td>
          <input type="checkbox" name="C1" value="un" />1
          <input type="checkbox" name="C2" value="deux" checked="checked" />2
          <input type="checkbox" name="C3" value="trois" />3
        </td>
      </tr>
      <tr>
        <td>Champ de saisie</td>
        <td>
          <input type="text" name="txtSaisie" size="20" value="qqs mots" />
        </td>
      </tr>
      <tr>
        <td>Mot de passe</td>
        <td>
          <input type="password" name="txtMdp" size="20" value="unMotDePasse" />
        </td>
      </tr>
      <tr>
        <td>Boîte de saisie</td>
        <td>
          <textarea rows="2" name="areaSaisie" cols="20">
ligne1
ligne2
ligne3
</textarea>
        </td>
      </tr>
      <tr>
        <td>combo</td>
        <td>
          <select size="1" name="cmbValeurs">
            <option value="1">choix1</option>
            <option selected="selected" value="2">choix2</option>
            <option value="3">choix3</option>
          </select>
        </td>
      </tr>
      <tr>
        <td>liste à choix simple</td>
        <td>
          <select size="3" name="lst1">
            <option selected="selected" value="1">liste1</option>
            <option value="2">liste2</option>
            <option value="3">liste3</option>
            <option value="4">liste4</option>
            <option value="5">liste5</option>
          </select>
        </td>
      </tr>
      <tr>
        <td>liste à choix multiple</td>
        <td>
          <select size="3" name="lst2" multiple="multiple">
            <option value="1" selected="selected">liste1</option>
            <option value="2">liste2</option>
            <option selected="selected" value="3">liste3</option>
            <option value="4">liste4</option>
            <option value="5">liste5</option>
          </select>
        </td>
      </tr>
      <tr>
        <td>bouton</td>
        <td>
          <input type="button" value="Effacer" name="cmdEffacer" onclick="effacer()" />
        </td>
      </tr>
      <tr>
        <td>envoyer</td>
        <td>
          <input type="submit" value="Envoyer" name="cmdRenvoyer" />
        </td>
      </tr>
      <tr>
        <td>rétablir</td>
        <td>
          <input type="reset" value="Rétablir" name="cmdRétablir" />
        </td>
      </tr>
    </table>
    <input type="hidden" name="secret" value="uneValeur" />
  </form>
</body>
</html>

视觉控件与 HTML 标签的对应关系如下:

控件
HTML 标签
表单
<form method="post" action="...">
输入字段
<input type="text" name="txtSaisie" size="20" value="几个字" />
隐藏输入框
<input type="password" name="txtMdp" size="20" value="unMotDePasse" />
多行输入框
<textarea rows="2" name="areaSaisie" cols="20">
第1行
第2行
第3行
</textarea>
单选按钮
<input type="radio" value="是" name="R1" />
<input type="radio" name="R1" value="否" checked="checked" />
复选框
<input type="checkbox" name="C1" value="一" />1
<input type="checkbox" name="C2" value="二" checked="checked" />2
<input type="checkbox" name="C3" value="三" />3
下拉菜单
<select size="1" name="cmbValeurs">
<option value="1">选项1</option>
<option selected="selected" value="2">选项2</option>
<option value="3">选项3</option>
</select>
单选列表
<select size="3" name="lst1">
<option selected="selected" value="1">列表1</option>
<option value="2">列表2</option>
<option value="3">列表3</option>
<option value="4">列表4</option>
<option value="5">列表5</option>
</select>
多选列表
<select size="3" name="lst2" multiple="multiple">
<option value="1">列表1</option>
<option value="2">列表2</option>
<option selected="selected" value="3">表3</option>
<option value="4">列表4</option>
<option value="5">列表5</option>
</select>
提交按钮
<input type="submit" value="发送" name="cmdRenvoyer" />
重置按钮
<input type="reset" value="重置" name="cmdRétablir" />
button 类型的按钮
<input type="button" value="清除" name="cmdEffacer" onclick="effacer()" />

让我们来回顾一下这些不同的标签:

2.5.2.1. 表单

表单

<form method="post" action="postFormulaire">
HTML标签
<form name="..." method="..." action="...">...</form>
属性
name="frmexemple":表单名称
method="...":浏览器用于将表单中收集的值发送至Web服务器的传输方式
action="...":URL,表单中收集的值将发送至此处。
Web表单由 <form>...</form> 标签包围。表单可以有一个名称(name="xx")。表单中所有控件均遵循此规则。 表单的目的是收集用户通过键盘/鼠标输入的信息,并将这些信息发送至某个 Web 服务器的 URL。具体是哪个?即 action="URL" 属性中指定的那个。 如果缺少该属性,信息将发送至包含该表单的文档所在的URL。Web客户端可以使用两种不同的方法——POST和GET——向Web服务器发送数据。 method="méthode" 属性(其中 method 等于 GET 或 POST) 该标签中的 <form> 属性会告知浏览器,应采用何种方法将表单中收集的信息发送至由 action="URL" 属性指定的 URL。 当未指定 action="method" 属性时,默认采用 action="GET" 方法。

2.5.2.2. 文本输入字段

输入字段
<input type="text" name="txtSaisie" size="20" value="几个字" />
<input type="password" name="txtMdp" size="20" value="unMotDePasse" />
 
标签 HTML
<input type="..." name="..." size=".." value=".."/>
input 标签适用于各种控件。正是 type 属性使这些不同的控件能够相互区分。
属性
type="text":指定这是一个文本输入框
type="password":输入框中的字符将被 * 符号替换。这是它与普通输入框的唯一区别。此类控件适用于密码输入。
size="20":字段中显示的字符数——不限制输入更多字符
name="txtSaisie":控件名称
value="几句话":将在输入框中显示的文本。

2.5.2.3. 多行输入框

多行输入框
<textarea rows="2" name="areaSaisie" cols="20">
第1行
第2行
第3行
</textarea>
 
标签 HTML
<textarea ...>文本</textarea>
显示一个多行输入框,初始时其中已包含文本
属性
rows="2":行数
cols="'20":列数
name="areaSaisie":控件名称

2.5.2.4. 单选按钮

单选按钮
<input type="radio" value="是" name="R1" />
<input type="radio" name="R1" value="否" checked="checked" />
 
HTML 标签
<input type="radio" attribut2="valeur2" ..../>文本
显示一个单选按钮,旁边带有文本
属性
name="radio":控件名称。名称相同的单选按钮构成互斥组:只能选中其中一个。
value="值":分配给单选按钮的值。请勿将此值与单选按钮旁显示的文本混淆,后者仅用于显示。
checked="checked":如果存在此关键字,单选按钮处于选中状态;否则未选中。

2.5.2.5. 复选框

复选框
<input type="checkbox" name="C1" value="一" />1
<input type="checkbox" name="C2" value="二" checked="checked" />2
<input type="checkbox" name="C3" value="三" />3
 
HTML标签
<input type="checkbox" attribut2="value2" ....>文本
显示一个复选框,旁边带有文本
属性
name="C1":控件名称。复选框可以具有相同的名称,也可以不具有相同的名称。具有相同名称的复选框构成一组关联的复选框。
value="值":分配给复选框的值。请勿将此值与单选按钮旁显示的文本混淆。后者仅用于显示。
checked="checked":如果存在此关键字,复选框处于选中状态;否则未选中。

2.5.2.6. 下拉列表(组合框)

下拉列表
<select size="1" name="cmbValeurs">
<option value="1">选项1</option>
<option selected="selected" value="2">选项2</option>
<option value="3">选项3</option>
</select>
 
HTML标签
<select size=".." name="..">
<option [selected="selected"] value=”v”>...</option>
...
</select>
将位于 <option>...</option> 标签之间的文本显示为列表
属性
name="cmbValeurs":控件名称。
size="1":可见列表项的数量。size="1" 使该列表相当于一个下拉列表框。
selected="selected":如果列表项包含此关键字,该项在列表中将显示为已选中。在上例中,当下拉列表首次显示时,列表项 choix2 将作为下拉列表的选中项显示。
value=”v”:如果该项被用户选中,则向服务器提交的值是 [v]。如果不存在此属性,则向服务器提交的是显示并选中的文本。

2.5.2.7. 单选列表

单选列表
<select size="3" name="lst1">
<option selected="selected" value="1">列表1</option>
<option value="2">列表2</option>
<option value="3">列表3</option>
<option value="4">列表4</option>
<option value="5">列表5</option>
</select>
 
标签 HTML
<select size=".." name="..">
<option [selected="selected"]>...</option>
...
</select>
<option>...</option> 标签之间的文本显示在列表中
属性
与仅显示一个项的下拉列表相同。该控件与前一个下拉列表的唯一区别在于其 size>1 属性。

2.5.2.8. 多选列表

单选列表
<select size="3" name="lst2" multiple="multiple">
<option value="1" selected="selected">列表1</option>
<option value="2">列表2</option>
<option selected="selected" value="3">列表3</option>
<option value="4">列表4</option>
<option value="5">列表5</option>
</select>
 
HTML标签
<select size=".." name=".." multiple="multiple">
<option [selected="selected"]>...</option>
...
</select>
<option>...</option> 标签之间的文本显示在列表中
标签之间的文本
multiple:允许在列表中选择多个元素。在上例中,元素 liste1 liste3 均被选中。

2.5.2.9. button 类型按钮

button 类型按钮
<input type="button" value="清除" name="cmdEffacer" onclick="effacer()" />
 
标签 HTML
<input type="button" value="..." name="..." onclick="effacer()" ..../>
属性
type="button":定义一个按钮控件。还有另外两种类型的按钮,即 submit reset
value="清除":按钮上显示的文本
onclick="函数()":用于定义用户点击按钮时要执行的函数。该函数属于显示的网页文档中定义的脚本。上述语法是 javascript 语法。如果脚本使用 vbscript 编写,则应写为 onclick="函数",不带括号。 若需向函数传递参数,语法保持不变:onclick="函数(val1, val2,...)"
在本例中,点击按钮 Effacer 将调用以下 JavaScript 函数 effacer
<script type="text/javascript">
function effacer() {
alert("您已点击了'清除'按钮");
}
</script>
函数 effacer 显示一条消息:

2.5.2.10. 提交按钮

提交按钮
<input type="submit" value="发送" name="cmdRenvoyer" />
 
标签 HTML
<input type="submit" value="发送" name="cmdRenvoyer" />
属性
type="submit":将该按钮定义为将表单数据发送至Web服务器的提交按钮。当客户端点击此按钮时,浏览器将根据<form>标签中method属性定义的方法,将表单数据发送至该标签action属性中定义的URL。
value="提交":按钮上显示的文本

2.5.2.11. 重置按钮

重置按钮
<input type="reset" value="重置" name="cmdRétablir" />
 
标签 HTML
<input type="reset" value="重置" name="cmdRétablir"/>
属性
type="reset":将该按钮定义为表单重置按钮。当用户点击此按钮时,浏览器将把表单恢复到初始接收时的状态。
value="重置":按钮上显示的文本

2.5.2.12. 隐藏字段

隐藏字段
<input type="hidden" name="secret" value="uneValeur" />
标签 HTML
<input type="hidden" name="..." value="..."/>
属性
type="hidden":指定这是一个隐藏字段。隐藏字段是表单的一部分,但不会显示给用户。不过,如果用户要求浏览器显示源代码,就会看到 <input type="hidden" value="..."> 标签,从而看到隐藏字段的值。
value="uneValeur":隐藏字段的值。
隐藏字段有何作用?它可让 Web 服务器在处理客户端的请求时保存信息。以一个 Web 购物应用为例。 客户在目录的首个页面上购买了第一件商品 art1,数量为 q1,随后跳转到目录的另一个页面。 为了记住客户已购买了 q1 art1 这两件商品,服务器可以将这两条信息放入新页面 Web 表单中的隐藏字段中。 在该新页面上,客户购买了 q2 和 art2 商品。当该第二个表单的数据被提交至服务器时,服务器不仅会收到 (q2,art2),还会收到同样作为隐藏字段存在于表单中的 (q1,art1)。 随后,Web服务器将把信息(q1,art1)和(q2,art2)放入一个新的隐藏字段中,并发送新的目录页面。以此类推。

2.5.3. Web客户端向Web服务器提交表单值

我们在前一节中提到,Web客户端有两种方法可将显示的表单值发送给Web服务器:方法GET和POST。让我们通过一个示例来看看这两种方法的区别。

2.5.3.1. 方法与GET方法

首先进行一次测试,在文档的 HTML 代码中,<form> 标签定义如下:


<form method="get" action="doNothing">

当用户点击按钮 [1] 时,表单中输入的值将被发送至 Spring 控制器 [2]。 我们看到表单的值将被发送至 URL [doNothing]:


<form method="get" action="doNothing">

[doNothing] 操作在 [MyController] [2] 控制器中定义如下:


    // ----------------------- 使数据流为空 [Content-Length=0]
    @RequestMapping(value = "/doNothing")
    @ResponseBody
    public void doNothing() {
}
  • 第 1 行:该操作处理 URL [/doNothing],因此实际上是 [/context/doNothing],其中 [context] 是 Web 应用程序的上下文或名称, 此处为 [/intro];
  • 第 3 行:注解 [@ResponseBody] 表示被注解方法的结果应直接发送给客户端;
  • 第 4 行:该方法不返回任何值。因此客户端将收到来自服务器的空响应。

我们只想了解浏览器如何将输入的值传输给 Web 服务器。为此,我们将使用 Chrome 中提供的调试工具。按下 CTRL-Shift-I(大写 Shift)即可激活该工具:

由于我们关注的是浏览器与Web服务器之间的网络交互,因此我们先打开上方的[Network]标签页,然后点击表单中的[Envoyer]按钮。 该按钮属于 [submit] 类型,位于 [form] 标签内。 浏览器响应点击时,会根据[form]标签的[action]属性中指定的URL [/intro/doNothing], 并使用GET属性中指定的GET方法。随后我们获得以下信息:

上图的屏幕截图显示了在点击 [envoyer] 按钮后,浏览器请求的 URL。 它确实请求了预期的 URL(即 [/intro/doNothing]),但在其后附加了表单中输入的值作为补充信息。为了获取更多信息,我们点击上面的链接:

在 [1, 2] 上方,我们可以看到浏览器发送的 HTTP 头部信息。此处已对其进行格式化处理。 若要查看这些标头的原始文本,请点击链接 [view source] [3, 4]。完整文本如下:

GET /intro/doNothing?R1=non&C2=deux&txtSaisie=qqs+mots&txtMdp=unMotDePasse&areaSaisie=ligne1%0D%0Aligne2%0D%0Aligne3%0D%0A&cmbValeurs=2&lst1=1&lst2=1&lst2=3&cmdRenvoyer=Envoyer&secret=uneValeur HTTP/1.1
Host: localhost:9000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Referer: http://localhost:9000/intro/exemple-05.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

我们发现了一些之前已经出现过的元素。还有一些元素是首次出现:

连接:keep-alive
客户端请求服务器在响应后不要关闭连接。这将允许客户端使用同一连接进行后续请求。连接不会无限期保持打开状态。如果长时间未使用,服务器将关闭该连接。
Referer
URL,即发起新请求时浏览器中显示的页面。

新内容位于 URL 后的信息第 1 行。 可见表单中的选项已反映在 URL 中。用户在表单中输入的值已通过 GET URL?param1=value1&param2=value2&... HTTP/1.1,其中 parami 是 Web 表单控件的名称(name 属性),valeuri 则是与其关联的值。 下面是一个三列表格:

  • 第 1 列:列出了示例中控件 HTML 的定义;
  • 第 2 列:显示该控件在浏览器中的显示效果;
  • 第 3 列:显示浏览器针对第 1 列控件发送给服务器的值,该值以示例中 GET 请求中的形式呈现。

控件 HTML


界面


返回值

<input type="radio" value="是" name="R1"/>是
<input type="radio" name="R1" value="否" checked="checked"/>否
R1=是
- 用户选中的单选按钮的 value 属性值。
<input type="checkbox" name="C1" value="一"/>1
<input type="checkbox" name="C2" value="二" checked="checked"/>2
<input type="checkbox" name="C3" value="三"/>3
C1=一
C2=二
- 用户勾选的复选框的 value 属性值
<input type="text" name="txtSaisie" size="20" value="几个词"/>
txtSaisie=Web+编程
- 用户在输入框中输入的文本。空格已被替换为 + 符号
<input type="password" name="txtMdp" size="20" value="unMotDePasse"/>
txtMdp=这是秘密
- 用户在输入框中输入的文本
<textarea rows="2" name="areaSaisie" cols="20">
第1行
第2行
第3行
</textarea>
areaSaisie=Web+编程+基础%0D%0A
Web+编程
- 用户在输入框中输入的文本。%OD%OA 是换行标记。空格已被 + 符号替换
<select size="1" name="cmbValeurs">
<option value='1'>选项1</option>
<option selected="selected" value='2'>选项2</option>
<option value='3'>选项3</option>
</select>
cmbValues=3
- 用户所选元素的 [value] 属性
<select size="3" name="lst1">
<option selected="selected" value='1'>列表1</option>
<option value='2'>列表2</option>
<option value='3'>列表3</option>
<option value='4'>列表4</option>
<option value='5'>列表5</option>
</select>
lst1=3
- 用户所选元素的 [value] 属性
<select size="3" name="lst2" multiple="multiple">
<option selected="selected" value='1'>列表1</option>
<option value='2'>列表2</option>
<option selected="selected" value='3'>列表3</option>
<option value='4'>列表4</option>
<option value='5'>列表5</option>
</select>
lst2=1
lst2=3
- 用户所选元素的 [value] 属性
<input type="submit" value="发送" name="cmdRenvoyer"/>
 
cmdRenvoyer=发送
- 用于将表单数据发送至服务器的按钮名称及其 value 属性
<input type="hidden" name="secret" value="uneValeur"/>
 
secret=一个值
- 隐藏字段的属性 value

2.5.3.2. 方法 POST

我们修改文档 HTML,使浏览器现在使用方法 POST 将表单值发送至 Web 服务器:


  <form method="post" action="doNothing">

我们按照 GET 方法填写表单,并通过 [Envoyer] 按钮将参数提交至服务器。 正如第 63 页上一段所述,我们在 Chrome 中可以查看浏览器发送的请求中的 HTTP 请求头:

POST /intro/doNothing HTTP/1.1
Host: localhost:9000
Connection: keep-alive
Content-Length: 172
Pragma: no-cache
Cache-Control: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://localhost:9000
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://localhost:9000/intro/exemple-05.html
Accept-Encoding: gzip, deflate
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

R1=non&C2=deux&txtSaisie=qqs+mots&txtMdp=unMotDePasse&areaSaisie=ligne1%0D%0Aligne2%0D%0Aligne3%0D%0A&cmbValeurs=2&lst1=1&lst2=1&lst2=3&cmdRenvoyer=Envoyer&secret=uneValeur

客户端的 HTTP 请求中出现了新内容:

POST URL HTTP/1.1
查询 GET 已被查询 POST 取代。 这些参数已不再出现在请求的第一行中。可以看出,它们现在被放置在(第15行)HTTP请求之后,其前有一行空行。它们的编码与GET请求中的编码完全相同。
Content-Length
“发送”的字符数,c.a.d。这是 Web 服务器在收到 HTTP 头部后,为获取客户端发送的文档所需读取的字符数。此处的文档即为表单值的列表。
Content-type
指定客户端在发送完 HTTP 头部后将发送的文档类型。类型 [application/x-www-form-urlencoded] 表示该文档包含表单值。

向 Web 服务器传输数据有两种方法:GET 和 POST。哪种方法更好?我们看到,如果表单值由浏览器通过 GET 方法发送, 浏览器会在其 Adresse 字段中以 URL 的形式显示所请求的 URL?param1=val1&param2=val2&.... 这既可以视为优点,也可以视为缺点:

  • 如果希望允许用户将此带参数的 URL 添加到收藏夹链接中,则视为优点;
  • 若不希望用户访问表单中的某些信息(例如隐藏字段),则属于缺点。

此后,我们在表单中将几乎完全采用 POST 方法。

2.6. Conclusion

本章介绍了Web开发中的若干基础概念:

  • 通过 HTTP 协议进行的客户端-服务器交互;
  • 使用 HTML 语言设计文档;
  • 设计数据输入表单。

我们通过一个示例了解了客户端如何向Web服务器发送信息。但本章尚未介绍服务器如何

  • 检索这些信息;
  • 处理这些信息;
  • 根据处理结果向客户端发送动态响应。

这是Web编程的范畴,我们将在下一章介绍Spring技术时探讨这一领域。