HTTP一点小知识

摘自计算机世界日报

Web应用程序开发技术

广东省教育科学研究所 陈 奇

  Internet 无疑是一种重要的信息传播媒体,随着其迅猛发展,将会有越来越多的企业、商团、政府机关、学校、科研机构需要在Internet 上建立自己的网点。建设一个网点,硬件上需要专用服务器、集线器、路由器,租用数据通信用的专线,软件上需要安装网络操作系统和Internet 服务器(www、ftp 和gopher 服务器),更为重要的是,需要编写大量的Internet 服务器应用程序。这种应用程序接收Internet 服务器传送过来的用户请求,从内部数据库检索出用户需要的数据,再将数据传送给用户。目前在Internet 上广泛应用的是www 系统,这种系统用HTML 文件格式(即通常所说的网页)传播信息,用统一资源定位符(URL) 连接世界各地计算机上的信息资源,按照HTTP 协议在浏览器和www 服务器之间通信。www 服务器又称为web 服务器,相应的服务器应用程序称为web 应用程序。在Windows 操作系统下,web 应用程序可分为两种类型:CGI(Common Gate Interface) 应用程序和ISAPI(NSAPI) 应用程序。这两种应用程序的功能是一样的,都是接收web 服务器传送过来的用户请求,作出响应,将用户需要的数据以网页或其它形式传送给用户。它们的区别在于,前者用标准输入输出或文件在web 服务器和web 应用程序之间传送信息,后者则是一种动态联接库程序(DLL),其数据可被web 服务器直接访问。ISAPI 是指Microsoft 的Internet 信息服务器(IIS) 编程接口,而NSAPI 则指Netscape 的Internet 服务器编程接口。本文将以Borland 公司的Delphi 编写ISAPI(NSAPI)程序的方法为例,介绍开发web 应用程序的原理和方法。这种应用程序在32 位的Windows 操作系统下运行,如果网点使用Windows NT,则本身就有IIS(包括www、ftp、gopher 三个服务器),开发、运行都很方便。

  * HTTP 协议和HTML 规范

  众所周知,Internet 的底层通信协议是TCP/IP 协议,在Internet 上传送的数据被划分为一个个的IP 数据报,每一个IP 数据报都指明信源和信宿的地址,沿途的网关按照信宿地址决定数据报的去向。而TCP 协议则为通信的双方建立一条虚电路,保证所有的数据报都能按正确的次序到达目的地。TCP/IP 只是实现计算机之间的二进制数据传输,对这些数据如何解释,则是上层协议的事情。开发web 应用程序的程序员并不需要过问TCP/IP 的工作,我们所必须了解的是其上层协议HTTP。

  HTTP 是一个应用层协议,目前广泛应用于web 浏览器和web 服务器之间的通信。HTTP 用字符串进行通信,所传送的信息称为HTTP 消息(HTTP-Message)。HTTP 消息有两种类型:浏览器传送给服务器的请求消息和服务器传送给浏览器的响应消息,其基本格式如下:请求消息:

Method Request-URI HTTP-Version ;请求行

general-header ;通用消息头

request-header ;请求消息头

entity-header ;实体消息头

;空行

message-body ;消息实体

其中三种消息头的次序是任意的。

响应消息:

HTTP-Version Status-Code Reason-Phrase ;状态行

general-header ;通用消息头

response-header ;响应消息头

entity-header ;实体消息头

;空行

message-body ;消息实体

  其中三种消息头的次序同样是任意的。

  对于请求消息,请求行是必不可少的,其余部分是可选的。请求行中的Method(方法)指定具体的请求操作,这些方法是HTTP 指定的,常用的方法有:

  GET 浏览器要求从服务器处获得信息。

  HEAD 作用等同于GET 但是服务器在响应时不必返回消息实体。通常用来

  测试信息资源是否可用,是否已更改。

  POST 浏览器向服务器传送信息。

  PUT 浏览器要求将消息中所包含的消息实体写入服务器。

  请求行中的Request-URI 是信息资源的定位符,类似于我们通常所说的" 网址",但网址通常是指某个网点IP 地址对应的域名,而URI 则要进一步指明网址下的路径、网页文件名、web 应用程序名等信息资源。请求行中的HTTP-Version 则指明浏览器所能执行的HTTP 协议版本号,多数是1.0 或1.1。例如,当我们要求访问某个abc 公司网址www.abc.com 上的网页xyz.htm 时,浏览器将会发出如下请求行:

  GET http://www.abc.com/xyz.htm HTTP/1.0

  请求消息中的通用消息头、请求消息头和实体消息头的一般格式是

  field-name : field-value

  也就是说,消息头由多个字段(field)组成,每个字段一行(HTTP 用回车换行符CRLF 作为每一行的结束标志),每行由字段名和字段值组成,彼此用冒号隔开。HTTP 对于每一个字段的意义及其对应的值都有详细的规定。可供使用的字段很多,但并不是每次发送请求消息都要使用所有的字段,而是根据需要使用其中的若干个。以下是一个请求消息的具体例子:

GET /default.htm HTTP/1.0

Host:http://default

Accept:text/html

User-Agent:Mozilla/2.0

(compatible; NEWT ActiveX; Win32)

对于请求消息,常用的字段有:

通用消息头:Cache-Control、Connection、Date

请求消息头: Accept、Authorization、

From、Host、If-Modified-Since、

Referer、User-Agent

实体消息头: Content-Encoding、

Content-Length、Content-Type、Expires

  限于篇幅,本文无法详细解释每一个字段的含义,有兴趣的读者可参阅参考文献[1]。

  请求消息中的消息实体(Message-Body) 是浏览器要传送给服务器的数据。使用GET 方法的请求消息一般不会有消息实体,而POST 和PUT 方法则一般会有消息实体,其具体内容可以是任何数据,由实体消息头中的字段指明其编码方式、类型和长度。

  对于响应消息,开头的状态行是必不可少的,其中开头的HTTP 版本号(HTTP-Version) 指明服务器所执行的HTTP 协议是哪个版本的,然后是由3 位数字组成的状态码(Status-Code),说明对浏览器请求消息的响应状态,最后的原因短语(Reason-Phrase) 是对状态码的简短文字说明。

  状态码的第一位数字定义响应状态的类型,可能的数值及其含义如下:

  * 1xx: 已接收到请求消息,正在处理中。

  * 2xx:对接收到的请求消息已成功地作出响应。

  * 3xx:必须采取进一步的行动才能完成请求。例如,请求消息中的URI 对应多个资源,或请求消息要访问的资源的URI 已改变,或必须通过代理服务器才能访问等。

  * 4xx:浏览器错误,请求消息语法错误或请求无法执行。

  * 5xx:服务器错误,服务器无法执行一个有效的请求。响应消息的状态行之后是与请求消息类似的、由各种字段名和字段值组成的消息头,对于响应消息,常用的字段有:

  通用消息头:Date

  响应消息头: Server、www-Authenticate、Location

  实体消息头: Allow、Content-Encoding、Content-Length、Content-Type、

  Expires、Last-Modified

  响应消息最后的实体(Message-Body) 部分是服务器按照浏览器的请求传送回来的数据,最常见的情况就是一个网页文件的内容。以下是一个响应消息的具体例子,最后一行是消息实体:

HTTP/1.0 200 OK

Server: Microsoft-PWS-95/2.0

Date: Thu, 15 Oct 1998 07:40:14 GMT

Content-Type: text/html

Content-Length: 21

Content:

Hi, glad to meet you!

  通常,上述消息通信过程是由用户浏览某个网页而发起的。网页是一个按照HTML 规范编写的纯文本文件,其基本框架结构如下:

< HTML >

< HEAD >

< TITLE > 网页标题< /TITLE >

< /HEAD >

< BODY >

... ... (网页的具体内容)

< /BODY >

< /HTML >

  关于HTML 规范的详细内容,可参见参考文献[2]。其实,我们完全可以通过实验方法来掌握这个规范。利用一些网页编辑软件(例如Microsoft 的FrontPage)可以直观地设计页面,然后观察所生成的HTML 文本,便可以轻而易举地学会编写HTML 文件。

  在每个网点上都有一个默认网页,当用户在浏览器上指定一个网点的网址时,浏览器向该网址上的web 服务器发出一个请求消息,其中的URI 即为该网点web 服务器的根目录。web 服务器接收到这个消息后,认为默认网页就是用户需要的信息资源并将之传送给浏览器。网页上包含各种链接,其基本格式是:

  < A href="URI" > 说明文字< /A >

  当用户点击这些链接时,就有可能启动浏览器与服务器的再次通信。链接中的URI 可能是同一个网页中的某个节点,或者是另一个网点的网址,或者是网点上的某一个网页或其它已存放在网点服务器上的文件。这些都由web 服务器自动作出响应,不需要web 应用程序的介入。对于编写web 应用程序的程序员,所关心的是一种称为" 查询"(Query) 的链接,例如, 在网页中设计如下链接:

  < A href=http://www.Tside.com/Scripts/handll.dll?MyQuery > 查询服务器< /A > 当用户点击这个链接时,浏览器将会向服务器发出类似于如下的请求消息

  GET /Scripts/handll.dll?MyQuery HTTP/1.0

  Host: http://www.Tside.com 网点www.Tside.com 上的web 服务器接收到这样的请求消息后, 将会向web 服务器根目录下的子目录Scripts 下的web 应用程序handll.dll 传送查询字串"MyQuery",由web 应用程序作出响应。通过这种方式,我们可将各种服务功能放置在网页文件中,用不同的查询字串表示不同的服务功能,由web 应用程序根据查询字串提供相应的服务。

  当需要提供给用户的服务功能较多时,还可以在上述查询链接的URI 中加入路径(PATH),例如:

  http://www.Tside.com/Scripts/handll.dll/MyPath?MyQuery

  其中设置的路径MyPath 可以是指服务器磁盘上的子目录,在上例的情况下,对应的将是服务器根目录下的子目录MyPath;也可以是虚拟的路径,仅供web 应用程序作为区分服务功能的分支使用。无论如何,web 服务器只是将路径名传递给web 应用程序,如何处理完全是应用程序自己的事情。

  另外一种重要的编程技术是利用网页中的表单(Form)。表单用来放置文字输入框、列表框、组合框、按钮、选择框等Windows 常见的控件。如同Windows 应用程序中的同类控件一样,这些控件的作用是提供交互操作功能,用户对于这些控件的操作结果将传送给web 应用程序。如下是一个包含表单的网页例子,其中的表单包含了一个文字输入框、一个打勾选择框和一个按钮:

< html >

< head > < title > Form Page Demo < /title > < /head >

< body >

< form action="http://default

/scripts/handler.dll" method="POST" >

< input type="text" name="text1" >

< input type="checkbox" name="check1" >

< input type="submit" name="button1" value="Submit" >

< /form >

< /body >

< /html >

  一个网页中可以有多个表单,每个表单以<form action=" …" method=" …">开始,以</form>结束。其中"action=" 后面引号内的文字指出接收表单操作结果的web 应用程序的URI,"method=" 后面引号内的文字指出浏览器向服务器发送该表单操作结果时所使用的方法,一般应该用POST。表单内可以有多个控件,每个控件的格式为

  < input type=" …" name=" …" value=" …" >

  其中的type 指明控件的类型,对于类型为submit 的控件,用户选中它将使浏览器发送当前操作的结果。Name 是设计者为控件取的名字,value 是控件的初始值,可有可无,但对于按钮,value 将是按钮上的文字。

  对于上例,假如我们在文字框中输入abc,选中选择框,然后按下类型为submit 的按钮时,浏览器将用POST 方法向网址为http://default 的服务器发送请求消息,请求消息中的消息实体将为如下内容:

  Text1=abc&Check1=ON&submit=Submit

  接收到请求消息的web 服务器将把这一串字符传递给web 应用程序。注意其格式是每个控件对应一个" 名值对":控件名= 控件值,各个名值对彼此用& 联结起来。Web 应用程序可以据此获知用户对控件的操作结果。

  各种控件的类型名称及其对应的值可查阅参考文献[2],或利用网页编辑器进行实验,此处不再一一赘述。

  * 用Delphi 开发web 应用程序

   以下的描述以Delphi 3.0 为例。

  1 .选择菜单File|New,在打开的对话框中选择图标Web Server Application,在再次打开的对话框中选择ISAPI/NSAPI Dynamic Link Library, Delphi 将为你建立一个web 应用程序的框架,其中包含了一个核心部件TwebModule,负责在web 服务器和你的应用程序之间传送数据。

  2 .现在我们可以为浏览器传送过来的查询或表单编写响应代码。选择WebModule,在Delphi 的开发工具Object Inspector 上将显示相应的属性(Properties),选择其中的属性Action,按下右端的小按钮,delphi 将打开一个对话框,让我们设计响应程序段。按下其中的按钮"Add",Delphi 将自动为我们的程序段起一个名字,例如WebActionItem1,并出现在对话框中。这种响应程序段Delphi 称为" 动作"(Action), 为了区别不同的动作,必须为每一个动作起一个名字,我们可以修改这个名字使之更有意义些。

  点击对话框中的动作名,Object Inspector 将会显示相应的属性。其中的MethodType 用来指定对何种方法的请求消息作出响应,可供选择的项目有mtAny、mtGet、mtHead、mtPost 和mtPut。其中后四项分别对应请求消息的方法GET、HEAD、POST 和PUT,若你想要对所有的方法都作出响应,则可选择mtAny。

  若你的动作是针对查询请求的,则还可以为属性PathInfo 设置路径名。例如,假设我们想要对查询

       http://www.Myside.com/Scripts/MyApp.dll/MyPath?MyQuery

  作出响应,则可将PathInfo 设置为"/MyPath"。注意在上述URI 中,www.MySide.com 是你的网点的网址,MyApp.dll 是你正在编写的应用程序名称,你的应用程序将存放在web 根目录下的子目录Scripts 中。

  接着是编写具体代码。Delphi 是通过事件响应来执行代码的,选择Object Inspector 中的标签Events,其中只有一个事件OnAction,双击右方的编辑框,Delphi 将在你的源程序中插入一个空白的过程,例如,

   procedure TWebModule1.WebModule1Web

  ActionItem1Action(Sender: TObject;Request: TWebRequest; Response:TWebResponse; var Handled: Boolean);

   begin

end;

  其中最重要的是两个调用参数Request 和Response。Request 是一个TWebRequest 对象,这种对象具有Accept,Authorization,CacheControl,Connection,... 等属性,这些属性对应于HTTP 请求消息中消息头的各个字段,这些属性的值就是这些字段的值。当上述响应过程被调用时,Delphi 根据web 服务器传送过来的消息头及消息实体,设置好对象Request 的各个属性值,然后作为调用参数传递给执行动作的过程。对于查询消息,表示查询目标的查询字串(如上面提到的MyQuery)将存放在Request 的属性Query 中。如果查询字串是由" 名值对" 所构成的,且各名值对之间用& 联结起来,例如

  name=dog&color=black

  则Delphi 还会将各个名值对拆开,再将每一对中的" 名" 和" 值" 存放在Request 的属性QueryFields 中。QueryFields 是Tstrings 类型的对象,其属性Names 和Values 分别存放各个名值对的" 名" 和" 值"。应用程序通过访问这些属性,便可知当前的查询是什么。

  如果接收到的请求消息是用POST 方法传送表单的操作结果,则如上所述,操作结果将是用& 联结多个名值对的一串字串,这一字串将存放在Request 的属性Content 中,并且Delphi 将分析这些名值对,将每一对中的" 名" 和" 值" 存放在Request 的属性ContentFields 中。ContentFields 和QueryFields 一样,都是TStrings 对象,通过其属性Names 和Values 可获得各个名值对的" 名" 和" 值"。

  以上分析使我们知道当前请求消息的详细情况。无论对请求如何处理,最终我们必须返回一些信息给浏览器。通常是应用程序将处理结果写成一个HTML 文件,逐行写入Respons 的属性Content 中。也可以利用Delphi 的控件PageProducer 编写HTML 文件,然后将PageProducer 的内容赋值给Response 的属性Content,返回给浏览器。

  Response 是TwebResponse 类型的对象,其属性Allow,ContentEncoding, ContentLength, ContentType, Date, Expires,... 等对应于响应消息中消息头的各个字段,应用程序如果需要设置这些字段的值,则可在以上响应过程中设置Response 的相应属性值。

  响应过程最后的参数Handled 用来说明响应动作是否已完成。

  3 .一个web 应用程序可对多个查询或表单作出响应,重复步骤2,我们可为各个不同的查询消息设计不同的响应动作。

  以上只是一个概略性的描述,实际编写起来要繁杂得多。有关细节,可查阅Delphi 的帮助文件。

  网点总是在运行过程中逐步完善、逐步增加功能的。在开发web 应用程序的过程中,应尽量避免在web 服务器上调试,以免影响网点的正常运行。我们可以利用一些在单机上就可以运行的个人web 服务器(例如Microsoft 的Personal Web Server),在单机上开发web 应用程序,调试成功之后再装入网点服务器。

  参考文献:

  1 .RFC 2068,Hypertext Transfer Protocol -- HTTP/1.1,January 1997

  2 .HTML 3.2 Reference Specification,W3C Recommendation 14-Jan-1997

posted on 2005-06-17 11:00  mbchn  阅读(405)  评论(0)    收藏  举报

导航