2004.11.3 AI程序设计 1
第 二 部分:第 8章 编写 CGI程序
第 8章 编写 CGI程序
本章分为基础和提高两部分内容 。 基础部分是指编
写 CGI程序的基础知识, 内容包括公共网关接口, CGI
程序及其测试, 还包括 CGI的程序功能分析及输入流分
析等 。 提高部分包括编写与测试 CGI应用程序, 内容有
信息传递方法, 高级 CGI应用程序, CGI程序的候选方
案, 加速 CGI应用程序, CGI程序的客户端, 使用
Javascipt对象, 安全性问题, 以及 CGI程序测试方法
实例等 。
2004.11.3 AI程序设计 2
第 二 部分:第 8章 编写 CGI程序
第 8章 编写 CGI程序
8.1 概述
8.2 编写 CGI程序基础
8.3 编写实用的 CGI应用程序
8.4 CGI应用程序测试实例
本章小结与习题
2004.11.3 AI程序设计 3
第 二 部分:第 8章 编写 CGI程序
8.1 概述
? 我们已经对 Visual Prolog 6中的基本编程比较熟悉 。 此处不再对诸如类,
接口, 对象等概念进行叙述 。 也不会介绍 Prolog中的回溯, 子句, 谓词等
概念 。 此处假设读者对这些概念是熟悉的 。 本章中的例子不牵涉任何类及对
象的创建, 所以对于那些不熟悉面向对象语言的读者来说, 应当比较易懂 。
? 最后一个例子用 Javascript处理客户端进程 。 它利用了 Javascript的面向对
象的特点 。 要阅读本章的内容, 读者最好要熟悉 Javascript的概念 。 要想详
细了解 Javascript的面向对象的特点, 不妨到下面这个网站去浏览一下,
http://www.webreference.com/js
? 本章中讲述的三个 CGI应用程序的例子都在文件 cgitutorial.zip中 。 如果电
脑中没有安装网络服务器, 可以用压缩文件中自带的 TinyWeb web
server。
2004.11.3 AI程序设计 4
第 二 部分:第 8章 编写 CGI程序
8.2 编写 CGI程序基础
8.2.1 公共网关接口
8.2.2 CGI程序
8.2.3 测试 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
8.2.5 测试 Example1
8.2.6 应用程序功能分析
8.2.7 输入流分析
2004.11.3 AI程序设计 5
第 二 部分:第 8章 编写 CGI程序
8.2.1 公共网关接口
? CGI是指公共网关接口, 是由世界万维网组织协会推荐的一种网络
服务器输入和输出信息流规范 。
? 正如我们所知道的, HTML 浏览器 ( 如 Internet Explorer,
Netscape) 是通过网络服务器发送和接受信息的 。 但是万维网的作
用并不只是读取一些超链接 HTML文件, 还需要有交互 。 即数据要
从浏览器流入和流出, 其中包括用户输入到浏览器窗口的信息 。 网
络服务器自己并不能解决所有问题, 那么它是如何处理这些交互信
息的呢?
2004.11.3 AI程序设计 6
第 二 部分:第 8章 编写 CGI程序
8.2.1 公共网关接口
? 网络服务器只是一个最基础的部分, 它可以输出 HTML文件到浏览
器, 包括其它的可识别的 MIME类型的文件 。 但它并不支持一些高
级操作, 比如进入数据库后台并返回数据库的请求给浏览器 。 为了
加强网络服务器的功能, 于是 CGI就产生了 。 服务器和同一台主机
上的 CGI程序进行对话后, CGI应用程序就可以替服务器完成它无
法完成的任务 。 比如进入数据库后台或执行复杂的搜索任务 。 应用
CGI的网络服务器通常要比那些不用 CGI的服务器可以发送更多的
信息 。
2004.11.3 AI程序设计 7
第 二 部分:第 8章 编写 CGI程序
8.2.1.1 公共网关
?,公共, 这个词代表能被所有的网络服务器接受的协议 。, 网关, 这
个词表示获得信息的行为, 就像通过一个门或关口进入另一端的程
序进程一样 。 抽象地说, 网络服务器是网关的一端, 而 CGI应用程
序表示网关的另一端, 它们通过 CGI彼此进行对话 。
2004.11.3 AI程序设计 8
第 二 部分:第 8章 编写 CGI程序
8.2.1.2 流
? 我们所说的术语流, 是指信息处理时, 每次只能处理数据队列中的一个字节 。
就好像一根管道, 它的直径只能允许通过一个字节, 这些字节以流的形式从
通道的另一端流出, 每次只有一个字节 。
? 正如水不能同时在水管中朝两个方向流动一样, 程序中的字节流也不能同时
进行读和写操作 。 数据或者从流中输出 ( 通常被称作可读流 ), 或者输入到
流中 ( 可写流 ), 但在单个程序中不能对同一个流同时进行读写操作 。
? 对 CGI程序来说, 字符是 ANSI格式的, 是八位的字符 。 这一点以后必须要注
意, 因为 Visual Prolog默认的是 UNICODE字符, 它是十六位字符 。
Visual Prolog中有专门用来接收 ANSI 字符流的谓词, 所以处理流并不是
问题 。 最后需要指出的是, 当我们浏览因特网的时候, 数据流通过因特网通
道流出到浏览器, 一次只有一个字节 。 我们甚至注意不到他的通过, 因为一
旦单个字符到达浏览器, 他们很快被组合起来, 所以我们看到的好象是那些
字节同时到达 。
2004.11.3 AI程序设计 9
第 二 部分:第 8章 编写 CGI程序
8.2.1.3 MIME类型
? MIME( Multipurpose Internet Mail Extensions) 比较确切的中文名称
为, 多用途互联网邮件扩展, 。 它是当前广泛应用的一种电子邮件技术规范,
基本内容定义于 RFC 2045-2049。
? MIME的目标之一是在数据文件和用于显示和编辑该文件类型的应用程序之
间建立联系 。 MIME通过为每种数据文件提供一个内容类型来实现这一点 。
? MIME类型是 Internet的命名数据类型的一个标准, 数据类型名由两部分组
成, 第一部分是说明数据的基本类型, 如图像, 视频, 音频, 文本等 。 由于
文本有不同的类型, 如 C源程序, 英文文本, 以及保存图像有多种格式, 所
以数据类型的第二部分是用来说明它的特殊类型 。 image/gif就是一个恰当
的例子 。 第一部分 images说明它是一个图像文件, 第二部分 gif说明它是以
GIF格式保存的 。
2004.11.3 AI程序设计 10
第 二 部分:第 8章 编写 CGI程序
8.2.1.3 MIME类型
? 在信息以流的形式被发
送到浏览器的同时, 服
务器会在流的开头加入
报头, 包 括 数 据 的
MIME类型 。 图 8.1就是
一个典型的例子, 它说
明了从服务器发送到浏
览器的信息的格式 。
图 8.1 服务器到浏览器的信息格式
2004.11.3 AI程序设计 11
第 二 部分:第 8章 编写 CGI程序
8.2.1.4 报头
? 在这里, 报头是指加在真正数据前的嵌入流中的一行文本, 该文本
后跟回车符 。 在上面的例子中, 数据上面那一行就是服务器发送到
浏览器的报头 。 之间用一行空白行把报头和正文分开 。
? 例子中报头的内容类型, content-type”这一行用来说明数据的
MIME类型, 这一行是专门应用于 CGI程序的, 因为当 Web服务器调用
一个 CGI应用程序的服务来提供要传送给浏览器的信息时, 它是忽
略 CGI程序提供的数据 MIME类型的 。 所以, 依照协议 CGI程序必须
在实际数据前加上报头 ( 用一个空白行分开 ) 。 这一点在编写 CGI
应用程序的时候必须注意, 因为不管是忘记了报头还是空白行 ( 在
实际数据和报头之间 ), 都会带来很多麻烦 。
2004.11.3 AI程序设计 12
第 二 部分:第 8章 编写 CGI程序
8.2.2 CGI程序
? CGI应用程序或网关程序 ( 见图
8.2) 是一个可执行程序 ( 通常是
exe文件 ), 它并不通过键盘或鼠
标接收任何信息 。 它并不是一个可
交互程序 ( 一个 CGI程序应该是一
个可交互的应用程序 ) 。 这种程序
通常被称作控制程序, 它通过标准
stdin流接收输入信息, 通过标准
stdout流输出 。 报头的其他部分
是在 CGI应用程序完成其工作后由
Web服务器加上的, 然后这些编
译好的信息被传送到浏览器 。
图 8.2 CGI应用程序或网关程序
2004.11.3 AI程序设计 13
第 二 部分:第 8章 编写 CGI程序
8.2.2 CGI程序
? 对 CGI程序来说, 一旦启动, CGI应用程序在执行后会自动结束,
而不需要任何输入来作用 。 这就像 DOS中的 xcopy,format命令一
样, 也是自动执行的 。
? CGI程序不仅仅是一个控制台应用程序, 它还等待接收符合规范的
输入信息, 并且输出信息也必须符合 CGI规范 。
? 在特定环境下, CGI程序也可以写成 DLL文件, 但这种变更是依赖
于 Web服务器的 。 甚至也可以是其它可能的外部变化 。 比如说, bat
文件也能做成 CGI程序, 前提是假设 Web服务器被设置为支持该文
件作为 CGI应用程序 。 我们不要陷入这些变更 。 在本章中我们把 CGI
程序都认为是符合 CGI规格的可执行控制台应用程序 。
2004.11.3 AI程序设计 14
第 二 部分:第 8章 编写 CGI程序
8.2.2.1 stdin与 stdout
? 在很多程序语言中都有对 stdin的定义, 它是一个专门用来接收来自
操作系统底层输入的流 。 程序 ( 比如用 Visual Prolog写的程序 ) 可
以读取 stdin流并且将该流给出的字符作为程序的输入 。 同样,
stdout也是一个由操作系统控制的流, 用来接收程序的输出信息 。
? 不同程序的 stdin和 stdout流可以连接在一起 。 例如, A程序写入信
息到 stdout,然后调用程序 B,程序 B用 stdin流接收由程序 A输出的
信息 。 这就是网络服务器最基础的转换机制 。 在前面的例子中, A就
代表网络服务器, B代表 CGI应用程序 。 而 stdin流和 stdout流通常
被放在一起称为控制台 。
2004.11.3 AI程序设计 15
第 二 部分:第 8章 编写 CGI程序
8.2.2.2 CGI中的信息流
? 信息流是在客户向服务器发送请求时产生的 。 就像前边提到的, 客户端会
把它的处理能力和所请求的信息通知 Web服务器 。 图 8.3说明了浏览器
( 客户 ) 向服务器发送的信息内容 。
图 8.3 浏览器(客户)
向服务器发送的信息
2004.11.3 AI程序设计 16
第 二 部分:第 8章 编写 CGI程序
8.2.2.3 环境变量
? 所有的操作系统都允许程序向计算机的 RAM中插入形如 name=value 的关
系式, 即所谓的环境变量 。 它是计算机 RAM( 随机存储器 ) 中的某个地方 。
如 DOS中的路径设置就是一个环境变量 。 我们可以通过 SET命令来查看
DOS(或 Windows)系统中的环境变量 。 仅需在命令行提示符下输入 SET
命令, 控制台就会显示所有可用的环境变量 ( 在显示列表中也可以看到路
径 ) 。
? 环境变量的概念中最好的一个优点就是我们可以设置自己的变量并且为它们
赋值 。
? 虽然值是字符串型的, 但并不必给出引用 。 所有值都是以字符串的形式存储
的, 并且以字符串的形式被 Visual Prolog程序取回 。 CGI应用程序中环境
变量的重要性在于服务器用各种环境变量来给 CGI 程序传送信息 。
2004.11.3 AI程序设计 17
第 二 部分:第 8章 编写 CGI程序
8.2.2.3 环境变量
? 我们已经知道服务器在调用 CGI应用程序之前把许多的数据放在 stdout流
里, 但是 CGI应用程序是如何知道要读入的数据有多少呢? 为了能做到这一
点, 服务器把数据的长度赋给一个叫 CONTENT-LENGTH的环境变量,
CGI程序通过这个变量的值得知要从流中读入的字符数量 。
? 除了内容长度, 许多 web服务器在启动一个 CGI应用程序之前还会先设置如
下的标准环境变量 。 有些服务器还可能对环境变量进行一些可能的变动, 这
主要取决于 web服务器 。 CGI程序反过来也可以寻找到这些环境变量并使用
相应的变量值 。
2004.11.3 AI程序设计 18
第 二 部分:第 8章 编写 CGI程序
8.2.3 测试 CGI程序
? 为了测试 CGI应用程序, 我们需要有一个安装好的服务器, 还要有
CGI程序 。 还必须支持 HTML文件, 它通过网络服务器激活一个 CGI
程序 。 当然, 这是随着程序的不同而不同的 。 学完这些例子后就会
加深对所支持文件的了解 。
? 前边已经讲过, CGI程序必须和服务器在同一台电脑上 。 考虑到安
全问题, 服务器只能通过一个特定的路径 ( 该路径称为 web路径 )
从主机上读取文件 。 所以, CGI程序 ( CGI可执行文件和 HTML文件 )
可以放在这样的路径上, 否则, 一些恶意用户可能会试着非法读取 。
2004.11.3 AI程序设计 19
第 二 部分:第 8章 编写 CGI程序
8.2.3 测试 CGI程序
? 同样, 网络服务器只能激活在正确路径上安装的 CGI程序 。 CGI程
序不是放在网络上的任何地方都可以的 ( 有些服务器可以, 但不是
全部 ) 。 现在, 所有服务器都有参数配置方法 ( 一个独立的配置文
件或,ini文件或者 windows注册表 ) 来设置各种参数 。 其中一个很
重要的参数就是 CGI程序的存放路径 。 当我们为 CGI程序瞄准了
Web服务器时, 要知道的第一件事就是该 Web服务器如何配置它的
CGI应用程序路径 。
- 服务器的选择
- CGI程序的存放位置
2004.11.3 AI程序设计 20
第 二 部分:第 8章 编写 CGI程序
8.2.3.1 服务器的选择
? 如上所述,Web服务器不只是一些复杂的软件,有许多免费的 Web
服务器可以选择。在
http://www.serverwatch.com/stypes/index.php/d2Vi上就列
出了很多。其中包括免费的软件和商业的软件。 Web服务器的复杂
性主要在于对 web路径的设置和对 Web服务器的 CGI程序路径的设
置。
? 安装服务器时同时也要在计算机上安装 TCP/IP协议(如果已经能浏
览因特网,说明已经装好)。一旦成功安装服务器之后,就可以通
过服务器的 IP地址或机器的域名来获得服务器上的数据。现在的
CGI程序能够与大部分的服务器一起很好地运行,包括微软的
windows 2000的 IIS5 web server。
2004.11.3 AI程序设计 21
第 二 部分:第 8章 编写 CGI程序
8.2.3.2 CGI程序的存放位置
? 确保 CGI程序存放在服务器的正确路径上。随着服务器的不同,这
些存放路径也是不同的。在本章,我们都认为是存放在下面这个路
径上 http://localhost/cgi-bin/ <APPNAME>,服务器上的浏览
器可通过这个 URL调用 CGI程序。
? 比如一个名叫 example1.exe的 CGI程序,那么它在服务器上的
URL应该是,http://localhost/cgi-bin/example1.exe,这样它
才是可达的。
2004.11.3 AI程序设计 22
第 二 部分:第 8章 编写 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
? 我们可以把 cgitutorial.zip中的 example.zip解压, 然后对这个 Visual
Prolog例子进行测试 。 也可以根据下面的指导步骤自己重新创建一个 。
1) 新建一个 Visual Prolog 项目, 注意 Ui Strategy 中是 Console,见
图 8.4所示 。
图 8.4 新建一个 Visual
Prolog 项目
2004.11.3 AI程序设计 23
第 二 部分:第 8章 编写 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
2) 观察主项目树
这时就会显示出主项
目树,其中包括由 VDE决
定的项目所需要的文件,
见图 8.5所示。
图 8.5 主项目树
2004.11.3 AI程序设计 24
第 二 部分:第 8章 编写 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
3) 建立项目
现在, 打开 build 菜单, 选中
Build菜单项或者 按组合键
Alt-F9。 随着建立过程的继续,
VDE 会调用相关的
PFC(Prolog Foundation
Classes)文档, 这些文档在
项目树中是看不到的 。 建立完
毕后的项目树如图 8.6所示 。
图 8.6 主项目树内容
2004.11.3 AI程序设计 25
第 二 部分:第 8章 编写 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
4)修改代码
双击项目树中的文件 example1.pro,并转向下面的代码,
clauses
run():-
console::init(),
succeed(),% place your own code here
end implement example1
goal
mainExe::run(example1::run),
可以清楚地看到,程序的主要目标调用了被称作 run的一个谓词,该谓词来
自 PFC模块 mainEXE。然后 PFC再激活 example1.pro中的 run()谓词。
看下面所示的程序,
run():-
console::init(),
succeed(),% place your own code here
2004.11.3 AI程序设计 26
第 二 部分:第 8章 编写 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
将上面的代码修改为如下的代码形式,
run():-
console::init(),% Line 1
OutStream = console::getConsoleOutputStream(),%Line 2
OutStream:setMode(stream::ansi(core::ansi)),
%Line 3
stdIO::write("Content-type,text/html\n"),%Line 4
stdIO::write("\n"),%Line 5
stdIO::write(
"<html><body><h1>Hello World!</h1></body></html>"),
% Line 6 (Don't forget the last dot!)
2004.11.3 AI程序设计 27
第 二 部分:第 8章 编写 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
5) 分析代码
现在让我们逐行分析这些代码,
第一行:这一行是 Visual Prolog 必须要有的, 所有的程序都要用到 console,
都必须以 console::init()开始 。
第二行:获得 stdout流对象, 并将其赋给叫做 Outstream的变量 。 前面讲过,
CGI程序希望发送它的输出到 stdout。 这一行执行后, 程序的输出将
自动指向 stdout( 所有的 write语句都会写入到 stdout) 。
第三行:在 Visual Prolog中, 默认的读写字符是 16位 UNICODE码, 但我们这
里需要的是 8位的 ANSI,这一行就是用来把 16位的 UNICODE码转化
成 8位的 ANSI码 。
2004.11.3 AI程序设计 28
第 二 部分:第 8章 编写 CGI程序
8.2.4 用 Visual Prolog 6创建 CGI程序
第四行:现在, CGI程序将第一行传送给发出请求的服务器 。 正如前面所讲到
的, 这些数据必须要有一个头部 。 头部用来设置数据的 MIME类型,
这里声明的数据类型是 text/html 的 MIME 类型, 注意这一行以换行
符 ( \n) 结束 。
第五行:报头中必须要用一空白行把头部和正文分开, 这是 CGI规范强制要求
的 。 所以 CGI程序发送一个空白行 ( \n) 。 在 CGI程序中经常会忘掉
这个要求, 这时可能会带来程序的错误执行 。
第六行:最后, 真实数据被写入到 stdout。 在这个例子中, 程序仅是简单创建
并发送 了一 个 HTML文件, 这 个文 件只 包括 两个 字:, Hello
World!, 。
2004.11.3 AI程序设计 29
第 二 部分:第 8章 编写 CGI程序
8.2.5 测试 Example1
? 依照前边所讲的把它放在服务器的正确路径上, 然后用合适的浏览
器进入下面这个 URL http://localhost/cgi-bin/example1.exe。
2004.11.3 AI程序设计 30
第 二 部分:第 8章 编写 CGI程序
8.2.6 应用程序功能分析
? 现在 CGI程序 Hello World (Example1)已经不是原先的样子了 。
它很简单, 但它可以充当很多复杂工作的基础 。 就拿最后一行为例,
这一行完成对 HTML页的创建并且发送 stdout流传送给服务器, 服
务器再把该页发送到发出请求的浏览器上 。 如果我们建立了一个数
据库并在数据库后台加入一些处理数据库的谓词, 就可以很轻松地
建成一个管理系统 。
2004.11.3 AI程序设计 31
第 二 部分:第 8章 编写 CGI程序
8.2.7 输入流分析
? 就像前面所指出的, 在这里我们忽略了服务器提供给程序的输入,
如果 CGI程序需要接收输入的话, 就必须对 stdin流进行读操作,
然后对信息进行处理 。 而且必须检查好在 CGI应用程序执行时设置
的环境变量 。 这可能有些复杂, 但幸运的是, 在 Visual Prolog 中
创建 CGI程序时我们根本不必做很多编程 。 例如, 如果想知道 CGI
程序接收了什么数据, 我们只需用谓词 cgi::getString()就可以了
( 项目中必须把 PFC中的程序包 cgi.pack包括在内 ) 。 如果想以更
方便的形式得到数据的话, 我们可以用 cgi::getParamList()谓词 。
一个个的数据被谓词 cgi::getParamList()组合为与环境变量相似
的名值对 。
2004.11.3 AI程序设计 32
第 二 部分:第 8章 编写 CGI程序
8.3 编写实用的 CGI应用程序
8.3.1 将信息从 HTML文件传输至 CGI程序
8.3.2 解释信息流的高级 CGI应用程序
8.3.3 信息从网络服务器到浏览器的传输
8.3.4 CGI应用程序简评
8.3.5 取代 CGI程序的候选方案
8.3.6 加速 CGI应用程序
8.3.7 CGI程序的客户端
8.3.8 使用 Javascript对象的高级 CGI应用程序
8.3.9 安全性问题
8.3.10 防止 CGI程序被盗链
8.3.11 小结
2004.11.3 AI程序设计 33
第 二 部分:第 8章 编写 CGI程序
8.3.1 将信息从 HTML文件传输至 CGI程序
? 在上面的图 8.3中, 我们展示了由浏览器向服务器发送信息流的
详细资料 。 大部分全球信息网的实践由发送这类请求的浏览器组
成 。 网络服务器依次处理这些请求同时把需要的 MIME类型数据送
给浏览器 。 问题到那里还未完结 。 在 HTML中, 我们能利用 HTML
FORM的元素嵌入特殊的交互窗体, 在 FORM元素内, 我们可以使
用像 INPUT,TEXTAREA 等的交互式窗体 。 进入这种窗体的数据
可以被分发到网络服务器 。 以这种形式在窗体中指定下列内容
( 参见图 8.7),
网络服务器对窗体信息应采取何种措施;
应使用哪种方法来实现该处理 。
2004.11.3 AI程序设计 34
第 二 部分:第 8章 编写 CGI程序
8.3.1 将信息从 HTML文件传输至 CGI程序
? 如 此 例 所 示,
ACTION 语句的后
面只是一个 URL,
它指定被网络服务
器调用的 CGI应用
程序 。 在上图给出
的例子中, 它是一
个被称为 prgm.pl
的 PERL应用程序 。
数据是被网络服务
器使用 GET方法传
给 prgm.pl的 。
图 8.7 窗体信息
2004.11.3 AI程序设计 35
第 二 部分:第 8章 编写 CGI程序
8.3.1.1 GET方法
? 在一个 GET程序中, 传递给 CGI程序的全部数据在从浏览器被送到
网络服务器的时候是被组合进 URL内 。 假如我们要传送三个变量 (名
字, 姓和电子邮件地址 )及他们的数值到被称为 myemailer.exe的
CGI应用程序, 这时 URL可以写作,
http://localhost/cgi-
bin/myemailer.exe?firstname=sabu&lastname=francis&em
ail=sabu@somewhere.com
2004.11.3 AI程序设计 36
第 二 部分:第 8章 编写 CGI程序
8.3.1.1 GET方法
? 当汇编这些 URL的时候,非法的字符要首先被转换到他们的 URL编码形
式。 举例来说,如果任意一个数值包含有一个空格,它必须被记为
%20,而一个冒号 (:) 将会被记为 %3A。 这一个编码形式叫做 URL编
码。在变量名和它的值之间利用间隔符号, =”分离。符号 (&) 被用于将
一个名字数值对与其它的名字数值对区分开来。一旦 URL到达网络服务
器,CGI程序将被触发,类似于本例中的 myemailer.exe,而且名字数
值对的整个列表经由一个叫作 QUERY_STRING的环境变量向前传递至
CGI应用程序,它给出了在网络服务器与 CGI应用程序通信的同时,服务
器使用的环境变量列表 )。 QUERY_STRING的值将会是在 "?" 之后出
现的任何值。在上例中,它会是,
firstname=sabu&lastname=francis&email=sabu@somewhere.com
2004.11.3 AI程序设计 37
第 二 部分:第 8章 编写 CGI程序
8.3.1.2 POST方法
? 在 POST 方法中, 使用各种交互式 HTML元素将数值传送给 Web服务器, 这
些交互式 HTML指定了 HTML FORM元素的有效的子元素 ( 例如 INPUT,
TEXTAREA 等 )。 这些数值被网络服务器接收, 并由服务器使用 stdout流
传递至指定的的 CGI程序 。 CGI 程序依序读它的 stdin流, 以获取这些流
包含的变量和数值 。 正如前面所解释的, 网络服务器的 stdout流是可读
的, 类似于被网络服务器调用的 CGI应用程序的 stdin 流 。
? 注意, 即使在 POST方法中, 定义 QUERY_STRING也是可以的 。 例如, 我
们能在应用程序名结尾的 "?" 之后定义一些参量, 就像在前面所举的例子
中显示 的一 样 。 因此, CGI 程序既 能从 stdin中 读取 数据, 也 能从
QUERY_STRING环境变量中读取数据 。 这一功能在我们的最后一例中 (在
cgitutorial.zip中的 Example3.zip)被用于区分各种不同类型的 POST处理
过程 。
2004.11.3 AI程序设计 38
第 二 部分:第 8章 编写 CGI程序
8.3.1.3 GET方法和 POST方法比较
? GET方法 是可标记的 。 这意味着整个 URL包含了所有需要被送往 CGI程序的
数据 。 缺点是以这种方式传送数据有一个上限 (大约 1000字符 )。 另一个
缺点是无论什么数据在被送往 CGI程序的过程中都是可见的 。
? POST方法的优点是它能处理大量信息 。 并且数据在传送过程中不易被查看
(除非使用探测工具 )。 而且如果我们使用一个可靠的服务器, 浏览器也已经
可靠连接到网络服务器, 此时即使使用探测工具可能也无法显示正向网络服
务器传送的数据 。 POST方法的缺点是我们通常得在 HTML中构造一个
HTML FORM元素, 并将这一 FORM元素用正确的 HTML交互式元素, 比如
INPUT,TEXTAREA等组装起来, 以使 POST方法正常工作 。 有的方法避
免直接构建一个 FORM元素, 但是那将使 HTML里面包含有结构庞大的
Javascript程式 。
2004.11.3 AI程序设计 39
第 二 部分:第 8章 编写 CGI程序
8.3.1.4 URL编码
a) 非 ASCII字符 (代码值 >128) 经由它们的 URL八进制编码 %xx进行编
码 。
b) 不能识别的或特殊的 ASCII字符将通过他们的 URL码编码 。 这些字
符是包括,‘ ~!#$%^&()+={}|[]\:";′<>?,/ TAB, 也就是除 @
* _ - 及, (小数点 )五个字符以外的所有字符 。
c) 空格符将被编码成正号 (+) 或者 %20。
2004.11.3 AI程序设计 40
第 二 部分:第 8章 编写 CGI程序
8.3.2 解释信息流的高级 CGI应用程序
? 我们可以将包含在 cgitutorial.zip中的 example2.zip文件解压到一个方便的
目录中,并在此查找 Visual Prolog文件。这个例子使用一个叫做 BLAT的实
用小程序 (一个免费的程序,和帮助文件一起包含在 cgi-bin目录下) 邮寄
表格的内容。我们能根据下面所给的描述写出一个这样的应用程序。
1) 首先, 我们可以按照在本章例子 example1创建过程所描述的步骤执行 。 这些
步骤执行结束后, 就要执行一附加步骤, 使 CGI 程序能够接收来自网络服务
器的输入 。 如前面所指出的, 我们需要求助于包含在 Visual Prolog中的
PFC(Prolog基类 )。 右击项目树上的 PFC 文件夹, 会显示一个文件对话框 。
通过 PFC文件夹进入 CGI文件夹, 点击 cgi.pack。 此时项目树中也应该显示
出 cgi.pack,然后重新构建程序 。
以上步骤实现后,就有可能使用 PFC谓词 cgi::getParamList()获得所有经由网
络服务器送至 CGI程序的名字数值对。谓词智能化地剖析数据,不考虑数据
被网络服务器传送时使用的是 POST方法还是 GET方法。
2004.11.3 AI程序设计 41
第 二 部分:第 8章 编写 CGI程序
8.3.2 解释信息流的高级 CGI应用程序
2) 双击 example2.pro,增加以下谓词,
class predicates
assembleParams:(namedValue_list List,string Seed) -
>string procedure(i,i),
clauses
assembleParams([],S)=S,
assembleParams([H|T],OldSeed)=Result:-
H=namedValue(N1,string(V1)),
NewSeed=string::concatList([OldSeed,N1,"=",V1,"\n\n"]),
!,
Result=assembleParams(T,NewSeed),
2004.11.3 AI程序设计 42
第 二 部分:第 8章 编写 CGI程序
8.3.2 解释信息流的高级 CGI应用程序
3) 将 run() 中的子句改变为如下的形式,
run():-
console::init(),
OutStream = console::getConsoleOutputStream(),
OutStream:setMode(stream::ansi(core::ansi)),
stdIO::write("Content-type,text/html\n\n"),
doIt(),
4) 增加一个新的谓词 doIt() 如下所示,
2004.11.3 AI程序设计 43
第 二 部分:第 8章 编写 CGI程序
8.3.2 解释信息流的高级 CGI应用程序
class predicates
doIt:(),
clauses
doIt():-
trap(file5x::file_str("blatparams.txt",BlatParams),_,fail),
PList=cgi::getParamList(),
S=assembleParams(PList," "),
file5x::filenameunique("abc",Unq),
file5x::file_str(Unq,S),
BlatCmd=string::concatList(["blat.exe ",Unq," ",BlatParams]),
platformSupport5x::system(BlatCmd),
doIt():-
stdIO::write("Could not find the file blatparams.txt",
" in the cgi-bin directory,Create this file"),
stdIO::write(" with just one line (NO carriage return at the end",
" of the line!),as shown in the example below:<BR><BR>"),
stdIO::write("<PRE>-to sabu@archsfa.com -server archsfa.com -f",
" sabu@somewhere.com</PRE><BR><BR>"),
stdIO::write(
"In the above line,replace sabu@archsfa.com with the email address",
" of the person to whom the form is to be sent to."),
stdIO::write(" Replace archsfa.com with the SMTP address",
" that can be used,and replace sabu@somewhere.com"),
stdIO::write(
" with the 'From' address that is to be used for the transaction,"
"All these addresses MUST be valid.<BR><BR>"),
stdIO::write(
" More parameters can be added to this file ",
"(e.g,the subject to be used,etc.) if you understand the BLAT utility"),
!,
2004.11.3 AI程序设计 44
第 二 部分:第 8章 编写 CGI程序
8.3.2 解释信息流的高级 CGI应用程序
5) 在程序中加入 5xPlatformsupport.pack和 5xFile.pack 。这一过
程与在本章前面描述的过程一致。这两个程序包是编译 CGI程序所必
需的。
6) 调用建立过程。这时会出现一些对话框,询问在编译时是否包含其
他包。回答“是”就表示全包含。
2004.11.3 AI程序设计 45
第 二 部分:第 8章 编写 CGI程序
8.3.2 解释信息流的高级 CGI应用程序
7) 让我们检查并试运行。与 Example1不同,本例中的 run()谓词非常小。它
只输出标准的 MIME类型 text/html头和一个空白行,然后直接将控制权交
给 doit()谓词。这是必需的,因为 doit() 有两个子句,第一个子句体是完成
主要工作的,第二个子句在找不到此项目需要的配置文件时被执行。 doit()
的第一行试图使用 file5x:file_str/2谓词读取配置文件。如果找不到该文件,
那么它将立刻失效并调用 doit()的第二个子句。 doit()的第二和第三行收集
输入变量及其数值的列表 (经由网络服务器传送 ),再从列表产生一个长的字
串。这一字串被存储在 cgi-bin目录下的一个专门文件中,并且这一文件的
内容将被 blat实用程序电子邮寄。前面提到的配置文件 blatparams.txt,包
含了文件内容被送达人的 EMAIL地址。
8) 这一例子有很多优点。我们能在许多 CGI程序环境中将这一程序作为通用的
捕获应用程序,任意形式的输入都能被接受并邮寄给所选择的任何人。
2004.11.3 AI程序设计 46
第 二 部分:第 8章 编写 CGI程序
8.3.3 信息从网络服务器到浏览器的传输
? 信息从 Web服务器到浏览器的传输总是以单一流发生吗?不总是这样 。 只有
最小的 HTML文件, 如在前面的例子中给出的那样, 从网络服务器到浏览器
的传送才是发生在一个流里 。 大多数的 HTML文件嵌有其他的 MIME类型, 例
如图像或 Javascript代码 。 HTML元素, 比如 <SCRIPT SRC="...">,
<IMG SRC="...">等触发和网络服务器的特殊连接 。 为显示或使用这些
特殊的 MIME类型, 浏览器与网络服务器建立单独的连接 。 这就是为什么浏
览器用一种多变的方式分别建立页面的各个部分 (例如图像 )的原因 。 如果需
要, 我们甚至可以为每一条信息流分别编写 CGI应用程序 。 举例来说, 我们
能分别编写出传送 Javascript 代码给浏览器的 CGI程序和用来传送图像的
程序 。 当我们学到本章节最后线程化讨论板 (threaded discussion board)
的例子的时候, 这将会是很有用的 。
2004.11.3 AI程序设计 47
第 二 部分:第 8章 编写 CGI程序
8.3.3 信息从网络服务器到浏览器的传输
? 但是我们有必要写这么多的 CGI程序以操作一个 HTML文件中的各种不同的
信息流吗? 没必要, 确实不需要为浏览器页面的每个信息流编写独立的 CGI
程序 。 实现方法有很多, 在最后一例 Example3.zip中, 我们将会了解到,
如何创建一个使用 QUERY_STRING来区别被送往浏览器的各种不同类型
信息流的 CGI应用程序 。 开始时, 谓词 run()将检查 QUERY_STRING并转
向执行依赖于传入数值的代码 。 因此, 即使我们只写了一个可执行文件, 因
为执行动作按照 QUERY_STRING给出的命令发生改变, 所以它像是具有
了 5个不同的 CGI程序 。
2004.11.3 AI程序设计 48
第 二 部分:第 8章 编写 CGI程序
8.3.4 CGI应用程序简评
? CGI应用程序几乎没有限度地扩充网络服务器的功能。
惟一的缺点是 CGI应用程序必须运行得非常快。如果它
花太多时间处理并输出数据,网络服务器将会暂停应用
程序并忽略任何 CGI应用程序在暂停之后可能提供的输
出。
2004.11.3 AI程序设计 49
第 二 部分:第 8章 编写 CGI程序
8.3.5 取代 CGI程序的候选方案
? 已经有尝试去改变规范, 比如使用管道和套接字 ( pipes
and sockets) 。 更高级的网络服务器有允许服务器本
身扩展处理 CGI流量的内建 API 应用程序接口 。 这些扩
展通常记为 DLL文件形式 。 这些措施全是为达到较高的
速度 。 然而也带来了兼容性问题 。
? 幸运的是, 核心 CGI规范事实上仍被所有网络服务器所
遵守 。 因此, 如果我们抵制住研究改变 CGI的诱惑, 并
且坚持 CGI主规范, 那么我们将能写出可在各种网络服
务器上使用的 CGI 程序 。
2004.11.3 AI程序设计 50
第 二 部分:第 8章 编写 CGI程序
8.3.6 加速 CGI应用程序
? cgitutorial.zip中的 Example1.zip和 Example2.zip例子完成了网
络服务器端所做的所有数据处理 。 这包括数据的描述信息 。 通过描
述信息, 我们实际上可以在客户端的浏览器窗囗中查询所有 HTML信
息 。 在网络服务器端集成全部的 HTML时常耗费大量时间 。 还有另一
个缺点是:已集成的 HTML不易再改变, 除非重新编译 CGI 程序本
身 。 要避免这一缺点的一个非常简单的方法就是以 Javascript的形
式送原始的数据元到浏览器, 并且让浏览器以最后显示的 HTML形
式, 实际执行所有的信息描述 。
2004.11.3 AI程序设计 51
第 二 部分:第 8章 编写 CGI程序
8.3.7 CGI程序的客户端
? CGI程序能在 Visual Prolog语言中开发, 并有助于客户端处理吗?
在这样的方案中, 使用了客户端处理, 我们可以使用 Javascript的帮
助 。 现在, Javascript是一种非常稳定的面向对象的语言 。 一个鲜
为人知的事实是 Javascript和 Visual Prolog实际上几乎使用相同
的语法, 而且这很可能被应用于客户端处理 。
-Visual Prolog论域与 Javascript对象之间的联系
-CGI程序工作过程
2004.11.3 AI程序设计 52
第 二 部分:第 8章 编写 CGI程序
8.3.7.1 Visual Prolog论域与 Javascript对象之间的联系
? Visual Prolog的一个令人惊奇的方面是它使用的论域可以被映射 (也就是重建 )在
Javascript中 。 甚至嵌套结构也可能被重建 。 惟一的例外是谓词论域 。 Visual
Prolog和 Javascript之间的这种独特连接很容易被开拓, 如下例所示,
Visual Prolog定义了一个论域,
nameStr= nm(string NameOfPoster,string Email),
相同的论域在 Javascript中可被表现为一个对象 。 这里是它的构造函数和一个用于
返回构造函数创建的对象的一个函数,
//注意:下面是一个名字为 jsobjs.js 的 Javascript 文件
// the constructor
function nm1( NameOfPoster,Email)
{
this.name=NameOfPoster;
this.email=Email; alert("Created a JS nm1 Object!");
}
2004.11.3 AI程序设计 53
第 二 部分:第 8章 编写 CGI程序
8.3.7.1 Visual Prolog论域与 Javascript对象之间的联系
//a function that creates an object using the above constructor
function nm(NameOfPoster,Email)
{
return new nm1(NameOfPoster,Email);
}
? 上述函数将被写入一个叫做 jsobjs.js的文件 。 该方案 (一个构造函数 + 一个函数 )将被
用于 Visual Prolog中定义的每个论域 。 让我们阐述采用这一方案的理由 。
? 假设有一个 Visual Prolog 谓词需要传送论域 nameStr 的一个约束变量给一个
Javascript信息流, 我们该怎么做?
class predicates
returnNameStr:(),
writeNameStr:(nameStr TheName),
clauses
returnNameStr():-
V=nm("sabu","sabu@somewhere.com"),%Line 1
writeNameStr(V),%Line 2
2004.11.3 AI程序设计 54
第 二 部分:第 8章 编写 CGI程序
8.3.7.1 Visual Prolog论域与 Javascript对象之间的联系
writeNameStr(V):-
stdio::write("Content-type,text/javascript \n\n"),%Line 3
stdio::write("V="),%Line 4
stdio::write(V),%Line 5
? 假定我们已经写了一个称为, myjs.exe”的已整合上述基本代码的 CGI程序 。
这个程序的源代码没有完整给出 。 这些作为一个练习留给读者 。 (提示:在
run()子句体中调用谓词 returnNameStr(),参见 Example1)。
? 现在让我们编写一个小的 HTML文件, 如下所示,
<html>
<script src="jsobjs.js"></script>
<script src="/cgi-bin/myjs.exe"></script>
<body>
</body>
</html>
2004.11.3 AI程序设计 55
第 二 部分:第 8章 编写 CGI程序
8.3.7.1 Visual Prolog论域与 Javascript对象之间的联系
? 可以发现,即使实际上是一个空白的 HTML文件,当我们将它装载入浏览器
(使用 HTTP 协议 )的时候,它会给出如图 8.8所示的一个提示框。
? 从而,我们成功地将 Visual Prolog的论域转换为 Javascript中的对象。我
们能要求 Javascript以更易接受的方式描述实际内容,而不会显示像上面
那样的提示框。
图 8.8 提示信息对话框
2004.11.3 AI程序设计 56
第 二 部分:第 8章 编写 CGI程序
8.3.7.2 CGI程序工作过程
? 主要过程开始于 returnNameStr()。 第 1行 中, 在论域 nameStr里建立了
一个变量 V 。 然后在 第 2行 中, 使用另外一个谓词 writeNameStr()将那个
变量经由 stdout输出 。 让我们看看谓词是怎么工作的 。 第 3行 是表头 (在最
后带有一个空白行 )用于送 MIME去请求浏览器 。 注意, 这个例子与之前所
有例子不同的是, MIME是 text/javascript而不是 text/html。 理由是不像
前面例子中的 HTML输出, 这一个 CGI程序正在耗尽 Javascript资源 。
? 第 4行 只是输出字符串:, V=” 意谓着 Javascript 定义一个变量, V”。
(注意这是一个 Javascript全局变量, 而且我们选择 V为它命名, 与
returnNameStr()中的 Visual Prolog 变量一样 )。
2004.11.3 AI程序设计 57
第 二 部分:第 8章 编写 CGI程序
8.3.7.2 CGI程序工作过程
? 第 5行写出 Visual Prolog的字符串变量 V。 这就是不可思议之处,Visual
Prolog内部论域到字符串的转化确保 namestr论域中约束变量的字符串表
示正好来自 Javascript指定的函数 nm(...)。 现在, 让我们将注意力转向使
用 CGI程序 (myjs.exe)的 HTML文件 。 开始的 <SCRIPT>语句调用先前写
好的 Javascript文件 jsobjs.js,该文件包含了映射 Visual Prolog域的
Javascript对象 。 一旦 jsobjs.js被浏览器解释, 它将在内部产生被称作
nm1的 Javascript 对象 (注意 Javascript对象叫做 nm1而不是 nm。 nm是
函数, nm1是类 )。 HTML文件的第二个语句 <SCRIPT>使用 SRC参数调
用 CGI应用程序 。 这个脚本为浏览器提供数据, 使它有所反应 。 从效率上
考虑, 我们能在 HTML文件中以下列 SCRIPT块代替第二个 SCRIPT语句 。
2004.11.3 AI程序设计 58
第 二 部分:第 8章 编写 CGI程序
8.3.7.2 CGI程序工作过程
<SCRIPT>
V =nm("sabu","sabu@somewhere.com")
</SCRIPT>
? 因为那是藉由我们的 CGI应用程序传送的 。 注意 Visual Prolog使用算符 nm
以区别 namestr论域, 并调用 Javascript中的函数 nm(...)。 此时函数
nm(...) 产生类 nm1的一个对象并将它指派给 Javascript的全局变量 V。
现在考虑一下文件 jsobjs.js中 nm1的构造函数, 当创建该类的对象时, 将
会出现一警告对话框 。 最后, 这是我们迄今为止客户端处理的一个最好例子 。
如果留意就会发现, CGI应用程序实际上什么都不做, 只是分派某一特定论
域的约束变量到浏览器, 而实际的处理发生在浏览器端, 这时, 相同的约束
变量被当作一个 Javascript对象再次出现 。
2004.11.3 AI程序设计 59
第 二 部分:第 8章 编写 CGI程序
8.3.8 使用 Javascript对象的高级 CGI应用程序
? 关于如何在 Visual Prolog语言中使用 Javascript对象的高级 CGI应用程序,
将 在 本 章 的 最 后 一 例 中 进 行 说 明, 包括 cgitutorial.zip 在 内 的
Example3.zip中包含了一个被称为 Discboard的高级 Visual Prolog 6项
目 。 在把 example3.zip 的内容解压到一个适当的文件夹之后, 将
discboard.prj6 载入 Visual Prolog VDE 并查阅它的源代码 。 这是一个
可执行的例子, 使我们方便有效地使用线程讨论板 (threaded discussion
board)。
? 不像为 Example1.zip和 Example2.zip提供的注释, Example3.zip中源
代码左边的注释和这里给出的说明很好地对该例子做了解释 。
-了解所需要的论域
- 查看执行情况
2004.11.3 AI程序设计 60
第 二 部分:第 8章 编写 CGI程序
8.3.8.1 了解所需要的论域
? 在大部分高级应用程序中,第一步要做的是构建一个恰当的服务与问题的数
据结构。这里使用的讨论黑板与 Matt Wright 构建的讨论黑板十分相似 (可
参见相关演示
http://www.scriptarchive.com/demos/wwwboard/wwwboard.htm
l )。
? 让我们了解一下线程讨论板 (threaded discussion board)的概念:首先
看起来它好像包含一连串的 post。顺便提一下,一个 post是一种进入讨论
板的许可证。然而,在更进一步的讨论中,我们将发现它实际上是一个树状
结构:每个 post将有多重应答作为其子 post。应答和 post均没有数目限制。
2004.11.3 AI程序设计 61
第 二 部分:第 8章 编写 CGI程序
8.3.8.1 了解所需要的论域
? 一个新的 post在技术上即是一个线程 thread。在它累积 post作为它的子
post的同时,它能隔离出单独的树 (当它及其子句有应答时,加入论域
infinitum ),一连串这样的独立树形线程 thread构成整个讨论板。
? 如果研究文件 discboard.cl所描述的论域,thrd论域描述了线程讨论板的
本质。如果研究该论域的最后一个参数,事实上,就会发现 thrdLis是一连
串的 thrd论域。因此,每个讨论板的主要 post的整个树结构都是在那一个
论域中。
? 当学习文件 discboard.cl时,thrd论域中的其余变元是自我解释的:他们
描述有关单一 post的其它问题,比如建立 post的人名,他 / 她的电子邮件
地址,post的日期,时间和 IP地址等。
2004.11.3 AI程序设计 62
第 二 部分:第 8章 编写 CGI程序
8.3.8.2 查看执行情况
? CGI应用程序的主要运行部分开始于谓词 execute(),位于模块
discboard.pro的末端。它要做的第一件事就是测试 QUERY_STRING环境
变量,然后重新定位执行程序为 exec()谓词的五个子句之一。根据
QUERY_STRING,程序将做出不同的响应。
? 因此,从实用的目的来看,这个 CGI程序可以起到五种不同应用软件的作用。
给出的 QUERY_STRING指令可能是这五者之一,addpost,replypost,
delpos,showtree和 showpost。 (QUERY_STRING与数据库文件名一
起传送,也用于线程化讨论 threaded discussion)。这 5个功能中,
showtree使用 Javascript对象,并且使用 MIME类型的 text/javascript将
他们传输到浏览器。
2004.11.3 AI程序设计 63
第 二 部分:第 8章 编写 CGI程序
8.3.8.2 查看执行情况
? 其余部分在功能上是相当直观的,是维持讨论板的数据所必须的。举例来说,
当新的 post被创建时,一个 thrd项被插入到讨论板数据库的一个特定链
(“__THREADS__”)。 当 post是对一个较早的 post的应答时,它将作为一
个子 post被插入到数据库中已经存在的 thrd项中。所有程序,返回 HTML
数据到浏览器,展现运算状态。为方便操作,四个功能 (addpost,
replypost,delpost和 showpost)被分别在它自己的 discboard.pro中编
写,用它自己的谓词类 class predicates划分。当读取 discboard.pro的代
码时,一次集中于一个区块,我们将很快掌握这四个功能。
? 但是,showtree需要做一些额外解释。
? 当 showTree()谓词运行时,它实际操作的是接收数据库顶端的 thrd posts
列表,并发送相同的列表,类似于一个 Javascript数组的函数调用。这一个
数组被文件 showposts.html接收,根据写在 dboard.js中的 Javascript代
码,该文件可以正确地对他们进行分析并变为 Javascript对象数组。
2004.11.3 AI程序设计 64
第 二 部分:第 8章 编写 CGI程序
8.3.8.2 查看执行情况
? 我们需要测试 dboard.js中的 Javascript代码以了解 Javascript函数 thr(...)
如何产生一个叫做 thrd1(...) 的 Javascript对象,以及这种函数数组如何结
束创建 thrd1(...)数组对象。 Javascript函数结束构造一个 Javascript对象的
基本方法与我们在 前面 看到的类似,一旦 Javascript对象的 thrd1数组就位,
在 dboard.js中将定义名叫 wrtAll(...)的 Javascript函数,该函数将使用数组
中的信息来构成所需要的 HTML,这些 HTML全部被显示在浏览器中。
(Dboard.js与 showposts.html是存在于网络文件夹中的文件 )
? 在理解这个 CGI程序的源代码之后,我们需要了解客户端处理部份,将学习必
需的客户端文件。这些文件是:控制客户端处理的 Javascript文件 dconfig.js
和 dboard.js。 dconfig.js是一个简单的配置文件,它的参数可被改变以适应
讨论板。 Dboard.js是主要的 Javascript文件,它包含了所有与 Visual
Prolog 源代码定义的 thrd论域和其他论域直接符合的构造函数及函数定义。
Dboard.js中的函数将从 CGI程序接收到的指令翻译为相应的 Javascript对象,
类似于本章前面的描述。
2004.11.3 AI程序设计 65
第 二 部分:第 8章 编写 CGI程序
8.3.8.2 查看执行情况
? 其次,我们还需要相应的 HTML文件,showposts.html,rep.html,
postmsg.html,replymsg.html,delmsg.html。最后,dboard.html也
是必需的,它集成所有的 HTML文件进一个框架之内。文件 postmsg.html,
replymsg.html和 delmsg.html包含用来做交互工作的表格。
? 当调用讨论板时,它在 showposts.html里面启动工作。该文件看起来像一个
空的 HTML 文件,但实际上是启动 CGI程序的 showtree 命令的文件,而且导
致 Javascript 函数数组定义的 Javascript对象,产生主要的讨论板树。一旦
讨论板的树正确地进入浏览器框架,它就做好了接受“点击”的准备。点一下
任何的信息链接,将在框架底部显示一个正确信息。而且顶端框架 (使用文件
rep.html) 包含讨论版所需要的其余交互式按钮和表格。
? rep.html包含很少的表格,而且调用正确 HTML表格的分发系统依赖于被调
用的函数。举例来说,Add Post按钮将会使 postmsg.html页面显示到一个
单独的窗囗中。所有的 HTML和 JS文件被充分注释,以便对他们所完成的功
能都能自我说明。
2004.11.3 AI程序设计 66
第 二 部分:第 8章 编写 CGI程序
8.3.9 安全性问题
? 在我们结束本章之前有一些注意事项,CGI程序没有与网络服务器相同的限
制 。 一个网络服务器只能在网络路径包含的范围内工作 。 除网络服务器的安
装目录外没有其他目录可存取一个网络服务器 。 如果你在网络路径外面保存
私人数据, 你可能要相当确定它不会将信息泄漏到任何其他刺探性的浏览器 。
然而, 一个 CGI 程序没有如此限制 。 它给出许多能力, 但是如果某一能力不
被正确约束, 那么 CGI程序就可能会使你的大部分数据暴露在外部视窗中 。
? CGI程序的另一个问题是:尽管写一个忽略运行环境的 CGI 应用程序是相当
容易的 。 但是这样的 CGI 程序可能很容易地被其他人盗用而你不知道 。 举例
来说, 本章的三个 CGI 程序发展就有此倾向 。 这可通过下面的例子说明:下
列是 HTML 超级链接编码,
? <a href="/cgi-bin/example1.exe">First example</a>
2004.11.3 AI程序设计 67
第 二 部分:第 8章 编写 CGI程序
8.3.9 安全性问题
? 它能应用于 example1.exe 所在的相同服务器 (访问命令 http:// site1) 。
如果那个 HTML在 site1上找到, 那么点击此链接会像期望的那样输出
,Hello World” 。 现在让我们把 HTML 代码放在另一个位置 http:// site2
上, 这时, 我们将不拷贝 example1.exe 到 site2的 cgi-bin目录 。 正如预期
的那样, 当你再点击这一链接时, 它将像预期的一样没反应, 因为
example1.exe 不是真正在 site2 上 。 现在让我们试着盗链
example1.exe。 我们将使用以下 HTML 代码,
<a href="http://site1/cgi-bin/example1.exe">
First Example on another server</a>
? 现在让我们点击这个链接,这时它会正常显示,即使 HTML代码在 Site2 上,
而且 Site2机器的任何地方都没有 example1.exe 。
2004.11.3 AI程序设计 68
第 二 部分:第 8章 编写 CGI程序
8.3.10 防止 CGI程序被盗链
? 避免 CGI 程 序 被 盗 链 的 一个 非 常 有效 的 方 法是 查 询一 个 被 称 为
HTTP_REFERER的环境变量 。 这个变量值包含 HTML文件在运行点提交至
CGI程序的相关信息 。 这能很容易被分析看是否网站有效, 而且 CGI程序可
能因此开始 。 让我们修改 example1并看一下如何能避免上面例子中的盗链 。
run():-
console::init(),%Line 1
OutStream = console::getConsoleOutputStream(),% Line 2
OutStream:setMode(stream::ansi(core::ansi)),% Line 3
stdIO::write("Content-type,text/html\n"),% Line 4
stdIO::write("\n"),% Line 5
run2(),!,
2004.11.3 AI程序设计 69
第 二 部分:第 8章 编写 CGI程序
8.3.10 防止 CGI程序被盗链
class predicates
run2:(),
checksOut:() determ,
clauses
run2():-
checksOut,
stdIO::write("<html><body>",
"<h1>Hello World!</h1></body></html>"),
!,%Line 6a
run2():-
stdIO::write("<html><body>",
" <h1>Access denied!</h1></body></html>"),
!,%Line 6b
checksOut():-
Refr=environment::getVariable("HTTP_REFERER"),
string5x::concat("http://localhost",_,Refr),
!,
2004.11.3 AI程序设计 70
第 二 部分:第 8章 编写 CGI程序
8.3.10 防止 CGI程序被盗链
? 为使用上例, 我们必须将 string5x和 cgi 程序包括进 example1项目内 (在
建立过程中, VDE可能会提醒给该项目添加更多的程序包 。 提示出现时只需
点击, Yes to all”即可 )。 在编译修改好的例子后, 我们将会发现只有当它
经由 URL为 http:// localhost的 HTML页面被显示时, example1.exe才会
工作 。 奇妙之处发生在 checksOut() 谓词中, 由它找到环境变量
HTTP_REFERER的值是什么 。 这一环境变量经由网络服务器传至 CGI 程序,
而且它包含呼叫 CGI程序网页的 URL。 checksOut() 谓词的第二行使用
concat谓词查看调用页面是否的确来自网址 http://localhost。 我们也可
以通过使用 URL为 http://127.0.0.1 ( 127.0.0.1是 localhost的 IP地址 )
试图存取脚本来检查上例, 这时将会发现脚本显示拒绝访问 Access Denie!
2004.11.3 AI程序设计 71
第 二 部分:第 8章 编写 CGI程序
8.3.11 小结
? CGI 程序给了网络服务器一个有力的路由扩充功能 。 它能以相当速
度运行, 但是如果我们使用 Javascript 的客户端处理, 大部分处
理将可能被送交给浏览器;留下 CGI程序仅做很少的工作 。 CGI 程
序不比特别设计的控制台应用程序运行得更快 。
2004.11.3 AI程序设计 72
第 二 部分:第 8章 编写 CGI程序
8.4 CGI应用程序测试实例
? 在本章, 用 Visual Prolog 6编写 CGI应用程序, 中提到了三个例子,
他们都在文件 cgitutorial.zip中 。 要调试这些程序, 电脑上需要安
装一个网络服务器 。
? 如果电脑中没有安装网络服务器, 可以使用压缩文件中自带的
TinyWeb网络服务器 。
? 这并不是说本章离不开 TinyWeb网络服务器 。 本章的主要目的是讨
论如何用 Visual Prolog 6编写 CGI应用程序, 教程自带的 TinyWeb
web server只是为了便于读者检查和调试例子 。
2004.11.3 AI程序设计 73
第 二 部分:第 8章 编写 CGI程序
8.4 CGI应用程序测试实例
8.4.1 安装 TinyWeb网络服务器
8.4.2 TinyWeb的根目录
8.4.3 TinyWeb的端口
8.4.4 调试例子程序
8.4.5 用其他网络服务器运行例子程序
2004.11.3 AI程序设计 74
第 二 部分:第 8章 编写 CGI程序
8.4.1 安装 TinyWeb网络服务器
? TinyWeb网络服务器是什么? 怎样安装?
? TinyWeb是 RIT实验室发布的一款出色的免费网络服务器 。 它是最
快的也许是免费网络服务器中最为严密的一种 。 TinyWeb与其他文
件是分离的 。 它没有也不需要独立的安装程序 。
? 如果你想调试 CGI应用程序, 一定要将它安装在一台端口为 80且没
有任何其他网络服务器运行的机器上 ( 最好是 Windows 2000操作
系统 ) 。
2004.11.3 AI程序设计 75
第 二 部分:第 8章 编写 CGI程序
8.4.1 安装 TinyWeb网络服务器
? 在 cgitutorial.zip中的 tutorial.zip文件中, 你将注意两个程序:一
个名为 tinder.exe, 另一个名为 tiny.exe 。 双击图标运行
tinder.exe。 它控制 TinyWeb 网络服务器 ( tiny.exe) 。 因为网
络服务器在端口 80上运行, 所以在试图运行 tinder.exe之前, 要确
保没有别的网络服务器在这个端口上运行 。 在 TinyWeb中无须完成
任何工作, 因为一切工作由 Tinder完成 。 Tinder在系统区运行 ( 通
常在电脑工具栏的右下角, 看起来像一个飞碟 ) 。 双击它将打开一
个小的控制面板 。 如果有必要, 可以在开始时将 Tinder设置为自动
运行 。 也可以通过控制面板设置网络服务器运行的端口和 web-root。
2004.11.3 AI程序设计 76
第 二 部分:第 8章 编写 CGI程序
8.4.2 TinyWeb的根目录
? 安装好 TinyWeb 之后, 可以通过一个浏览器 ( 如 Internet
Explorer) 将其打开, 其根 ( root) 的 URL为 http://localhost,
然后进行程序测试 。 在浏览器中列出的文件应当在 TinyWeb所属目
录的子目录里, 形如,index.html 。
2004.11.3 AI程序设计 77
第 二 部分:第 8章 编写 CGI程序
8.4.3 TinyWeb的端口
? 端口是指一种特殊的 TCP-IP手段, 用于运行特殊的互联网协议
( 可用的互联网协议有很多, 例如 POP,SMTP等 。 万维网是运行
于 HTTP协议之上的 。 ) HTTP协议的默认端口是端口 80。 如果在
计算机上已经有一个网络服务器在这个端口运行, 可以改变一下这
个网络服务器的端口号, 比如, 改成 81;如果想通过 TinyWeb网
络 服 务 器 进 入 HTTP 协议, 将 需 要 给 出 如 下 地 址,
http://localhost:81。
2004.11.3 AI程序设计 78
第 二 部分:第 8章 编写 CGI程序
8.4.4 调试例子程序
? 本教程中编译过的例子已经放在 web目录 ( 你现在阅读的这个文件
也是在这个目录下 ) 下的 cgi-bin文件夹中 。 确认 TinyWeb网络服
务器正常运行之后, 把这个地址 http//localhost/输入到浏览器的
地址栏中 。 按照浏览器中所列出的说明, 就可以运行所有的例子了 。
2004.11.3 AI程序设计 79
第 二 部分:第 8章 编写 CGI程序
8.4.5 用其他网络服务器运行例子程序
? 用别的网络服务器 ( 如微软的个人网络服务器, 或者 IIS等 ) 运行
这些例子同样很容易 。 只要保证在分离相关文件时被分离的网络路
径是该网络服务器所用的网络根路径形式 。 同样要保证网络路径中
的 cgi-bin路径应当是能被接受的可执行脚本形式, 并且通过路径
URL:/cgi-bin/ 可以到达 。
2004.11.3 AI程序设计 80
第 二 部分:第 8章 编写 CGI程序
本章小结
本章分为基础和提高两部分内容 。 基础部分是指编写 CGI程序的
基础知识, 内容包括公共网关接口, CGI程序及其测试, 还包括
CGI的程序功能分析及输入流分析等 。 提高部分包括编写与测试
CGI应用程序, 内容有信息传递方法, 高级 CGI应用程序, CGI程
序的候选方案, 加速 CGI应用程序, CGI程序的客户端, 使用
Javascipt对象, 安全性问题, 以及 CGI程序测试方法实例等 。
2004.11.3 AI程序设计 81
第 二 部分:第 8章 编写 CGI程序
习 题
1,用 Visual Prolog 6创建, 建立, 调试并运行一个 CGI程序 。
2,把 cgitutorial.zip中的 example.zip解压, 然后对这个 Visual Prolog例子进行测试
3,完成 8.3.7.1节中留给读者的练习 ( 即,8.3.7.1节已经写了一个称为, myjs.exe”的
CGI程序 。 这个程序的源代码没有完整给出, 请读者完成剩余的工作 ) 。
4,何谓, 公共网关,? 何谓, MIME类型,?
5,测试一个 CGI程序时, 如何选择服务器?
6,在 CGI程序中, stdin和 stdout是何含义? 如何设置和访问环境变量?
7,试解释 CGI程序中的, 输入流, 。
8,何谓 GET方法, POST方法? URL编码的含义是什么?
9,如何理解 Visual Prolog的论域与 Javascript对象之间的联系?
10,在编写一个 CGI程序过程中, 如何保证安全性? 如何防止 CGI程序被盗链?
11,如何用 TinyWeb网络服务器来调试一个 CGI程序过程?