HTTP 协议定义了客户端和服务器之间交互的消息内容和步骤,其基本思路非常简单。首先,客户端会向服务器发送请求消息(图1.4)。请求消息中包含的内容是“对什么”和“进行怎样的操作”两个部分。其中相当于“对什么”的部分称为URI。
一般来说,URI 的内容是一个存放网页数据的文件名或者是一个CGI 程序的文件名,例如 “/dir1/file1.html”“/dir1/program1.cgi”等。不过,URI 不仅限于此,也可以直接使用 “http:”开头的URL 来作为URI。换句话说就是,这里可以写各种访问目标,而这些访问目标统称为URI。
相当于接下来“进行怎样的操作”的部分称为方法。方法表示需要让Web 服务器完成怎样的工作,其中典型的例子包括读取URI 表示的数据、将客户端输入的数据发送给URI 表示的程序等。表1.1 列举了主要的方法,通过这张表大家应该能够理解通过方法可以执行怎样的操作。
除了图1.4 中的内容之外,HTTP 消息中还有一些用来表示附加信息的头字段。客户端向Web 服务器发送数据时,会先发送头字段,然后再发送数据。不过,头字段属于可有可无的附加信息,因此我们暂时跳过。
收到请求消息之后,Web 服务器会对其中的内容进行解析,通过URI和方法来判断“对什么”“进行怎样的操作”,并根据这些要求来完成自己的工作,然后将结果存放在响应消息中。在响应消息的开头有一个状态码,它用来表示操作的执行结果是成功还是发生了错误。当我们访问Web 服务器时,遇到找不到的文件就会显示出404 Not Found 的错误信息,其实这就是状态码。状态码后面就是头字段和网页数据。响应消息会被发送回客户端,客户端收到之后,浏览器会从消息中读出所需的数据并显示在屏幕上。到这里,HTTP 的整个工作就完成了。
现在大家应该已经了解了HTTP 的全貌,下面我们再补充一些关于HTTP 方法的知识。表1.1 列出的方法中,最常用的一个就是GET 方法了。一般当我们访问Web 服务器获取网页数据时,使用的就是GET 方法。所谓一般的访问过程大概就是这样的:首先,在请求消息中写上GET 方法,然后在URI 中写上存放网页数据的文件名“/dir1/file1.html”,这就表示我们需要获取/dir1/file1.html 文件中的数据。当Web 服务器收到消息后,会打开/dir1/file1.html 文件并读取出里面的数据,然后将读出的数据存放到响应消息中,并返回给客户端。最后,客户端浏览器会收到这些数据并显示在屏幕上。
还有一个经常使用的方法就是POST。我们在表单中填写数据并将其发送给Web 服务器时就会使用这个方法。当我们在网上商城填写收货地址和姓名,或者是在网上填写问卷时,都会遇到带有输入框的网页,而这些可以输入信息的部分就是表单。
使用POST 方法时,URI 会指向Web 服务器中运行的一个应用程序的文件名,典型的例子包括“index.cgi”“index.php”等。然后,在请求消息中,除了方法和URI 之外,还要加上传递给应用程序和脚本的数据。这里的数据也就是用户在输入框里填写的信息。
当服务器收到消息后,Web 服务器会将请求消息中的数据发送给URI 指定的应用程序。最后,Web 服务器从应用程序接收输出的结果,会将它存放到响应消息中并返回给客户端。
前面两个方法属于HTTP 的典型用法,除此之外的其他方法在互联网上几乎见不到使用的例子。因此,只要理解了这两个方法,就能够应付大部分情况了,但如果可以,还是推荐大家看一看表1.1 中所有方法的说明,思考一下它们的含义,以便理解HTTP 协议具备的所有功能。
如果只有GET 和POST 方法,我们就只能从Web 服务器中获取网页数据,以及将网页输入框中的信息发送给Web 服务器,而有了PUT 和DELETE 方法,就能够从客户端修改或者删除Web 服务器上的文件。有了这些功能,我们甚至可以将Web 服务器当成文件服务器来用。当然,出于安全上的原因,或者是支持GET 和POST 之外的方法的客户端没有广泛普及之类的原因,一般我们并不会碰到这样的用法,但大家应该能够看出,HTTP 协议其实蕴藏着很多的可能性。
生成HTTP 请求消息理解了HTTP 的基本知识之后,让我们回到对浏览器本身的探索中来。
对URL 进行解析之后,浏览器确定了Web 服务器和文件名,接下来就是根据这些信息来生成HTTP 请求消息了。实际上,HTTP 消息在格式上是有严格规定的,因此浏览器会按照规定的格式来生成请求消息(图1.5)。
首先,请求消息的第一行称为请求行。这里的重点是最开头的方法,方法可以告诉Web 服务器它应该进行怎样的操作。不过这里必须先解决一个问题,那就是方法有很多种,我们必须先判断应该选用其中的哪一种。
解决这个问题的关键在于浏览器的工作状态。这次探索之旅是从在浏览器顶部的地址栏中输入网址开始的,但浏览器并非只有在这一种场景下才会向Web 服务器发送请求消息。比如点击网页中的超级链接,或者在表单中填写信息后点击“提交”按钮,这些场景都会触发浏览器的工作,而选用哪种方法也是根据场景来确定的。
我们的场景是在地址栏中输入网址并显示网页,因此这里应该使用GET方法。点击超级链接的场景中也是使用GET 方法。如果是表单,在HTML源代码中会在表单的属性中指定使用哪种方法来发送请求,可能是GET 也可能是POST(图1.6)。
写好方法之后,加一个空格,然后写URI。URI 部分的格式如下,一般是文件和程序的路径名。
/< 目录名>/…/< 文件名>
前面已经讲过,路径名一般来说已经包含在URL 中了,因此只要从URL 中提取出来原封不动地写上去就好了。
第一行的末尾需要写上HTTP 的版本号,这是为了表示该消息是基于哪个版本的HTTP 规格编写的。到此为止,第一行就结束了。
第二行开始为消息头。尽管通过第一行我们就可以大致理解请求的内容,但有些情况下还需要一些额外的详细信息,而消息头的功能就是用来存放这些信息。消息头的规格中定义了很多项目,如日期、客户端支持的数据类型、语言、压缩格式、客户端和服务器的软件名称和版本、数据有效期和最后更新时间等。这些项目表示的都是非常细节的信息,因此要想准确理解这些信息的意思,就需要对HTTP 协议有非常深入的了解。表1.2中列举了主要的头字段供大家参考,但不必全部弄明白。消息头中的内容随着浏览器类型、版本号、设置等的不同而不同,大多数情况下消息头的长度为几行到十几行不等。
写完消息头之后,还需要添加一个完全没有内容的空行,然后写上需要发送的数据。这一部分称为消息体,也就是消息的主体。不过,在使用GET 方法的情况下,仅凭方法和URI,Web 服务器就能够判断需要进行怎样的操作,因此消息体中不需要填写任何数据。消息体结束之后,整个消息也就结束了。