如何知道CGI程序要浏览器输入数据?(150分)

H

Hahaman

Unregistered / Unconfirmed
GUEST, unregistred user!
我在写一个有浏览器功能的程序,HTM文本很容易得到,
但现在网上的CGI程序很多.请问各位INTERNET高手,我如何通过
DELPHI和CGI程序打交道呢?例如我们现在用的浏览器是自己写的,
进入大富翁论坛的时候浏览器是怎样和CGI打交道的.
最好请附上点小程序!谢谢!
 
浏览器要通过普通的URL以Get方法或者Form的targetURL,
以Get/POST的形式和 CGI打交道
I have the detailed specification about this, if you want, I can
mail to you. Now let me explain it in brief.

1.Get Method is simply, Example:
&ltIMG SRC="/cgi-bin/genimage.exe?NAME=TITLE.GIF"&gt
You can simply connect to web server: then use the following
command to request:
"Get /cgi-bin/genimage.exe?NAME=TITLE.GIF HTTP/1.1

"
(There's a blank line)

2. POST method is a bit difficult.
Example:
HTML page:
&ltForm method="POST" action="/cgi-bin/genimage.exe"&gt

Your browser can communicate with cgi this way: connect to
web server, then use the following command:
"PUT /cgi-bin/genimage.exe HTTP/1.1
Content-length=16
NAME=TITLE.GIF

"

If there's over 1 param to be passed, you can use & to connect them

3. In you cgi program part,
A. Get method:
Your program will read the string after "?" from Environment,
which name is "Query-String"
Query-String="NAME=TITLE.GIF"
So you can not pass too much data via this way

B. POST method
Your cgi program will get the data passed by POST method
from the standard input, you will read the string from
stdin:
"NAME=TITLE.GIF"

C. How does your cgi program answer browser?
It does this by write the result to the standard output,
the data format is defiened in MIME, ex.
you return a HTML page:
Write this string to stdout:
"
Content-type=text/html
Content-length=234

<HTML>
<HEAD>
<TITLE>
Test cgi
</TITLE>
</HEAD>
<BODY>
This is a test
</BODY>

"
 
Ohh, I forget to use &lt and &gt escape sequences, so you can't see them, :(

&ltHTML&gt
&ltHEADL&gt
&ltTITLE&gt
This is a test
&lt/TITLE&gt
&lt/HEADL&gt
&ltBODY&gt
Test page, :)
&lt/BODY&gt
&lt/HTML&gt
 
多谢pegasus大虾的回答.但小弟水平欠佳,您的回答我似乎
不太明白,我要写的是有浏览器功能的程序.我想知道当前的
网页是否有CGI程序,如何知道CGI要什么样的数据和我如何
将数据发送该CGI程序.
恭候pegasus大虾和其他大虾的指教.



 
1. If there's a URL contains ?, then it might be a cgi driven url,
这种情况下, 浏览器不用费什么脑筋, 直接使用这个URL向服务
器要数据就行了

2. Form action="" 中指出的URL, 则肯定是cgi,
这时的使用方法是:
把Form中的 参数和值组织成一个串,

按照Method="Get" or Method="POST"的要求,
根据我上面介绍的方法, 发送给Web Server,

例如:
Form中含有一个编辑框, 一个选择框:
&ltinput name="UserID", value=""&gt
&ltSelect name="Hobby"&gt
&ltoption value="Math"&gt
&loption value="Physics"&gt
&ltoption value="Computer Science"&gt
&ltoption value="Music"&gt
&/Select&gt

假设用户输入了UserID is "pegasus",
选择了Hobby is "Computer Science",
那么浏览器组织的字符串就是:
UserID=pegasus&Hobby=Computer+Science

如果Action is "/cgi-bin/test.cgi", method is "GET",
then you just send this string to web server:

"Get /cgi-bin/test.cgi?UserID=pegasus&Hobby=Computer+Science HTTP/1.1

"
ok了
 
多谢大虾的指点,真是"柳暗花明又一春"
能否请大虾写一个和本论坛的CGI打交道的小程序
Email给我







 
pegasus兄:
补充一句:263上的信箱不知道为什么用不了
请寄给我的朋友:xftang@shtdu.edu.cn
 
pegasus兄:
你在吗?@_@
 
下面是本人写的一本书的一部分,希望对你有帮助
我们先假设有这样一个功能需要实现:
建立一个站点,其中需要用户输入用户名称和密码已确认是否是会员,
如果是会员,就显示"欢迎进入"的页面,否则显示"谢谢使用,请先注册"。
我们考虑用CGI来实现,可以按下步骤来做:
第一步:编写符合要求功能的CGI程序TEST.EXE。
第二步:建立好登录页面input.html,使用一个FROM,内设两个输入框,一个为用户名,一个为密码,链接到CGI程序 TEST.EXE。
第三步:测试试验
经过这三步制成的网页,用户在使用时机器的具体实现方法是这样的:
用户在客户端的WWW浏览器浏览到网页input.html ,屏幕上出现input.html的内容,用户输入用户名及密码,并选择"上传",输入的内容通过TCPIP的网络传入服务器WWW的SERVER软件,软件将上传内容存入环境变量,同时启动CGI程序,CGI程序在服务器上运行,从环境变量中读出对方的各种信息,包括对方的IP地址、使用的浏览软件以及输入的用户名和密码等信息,CGI程序在将环境变量传来的信息处理分类后整理除用户名和密码,与服务器硬盘中贮存的用户名/密码数据库的内容对比,如果相同,产生"欢迎进入"的页面,否则产生"谢谢使用,请先注册"的页面,产生的页面数据传入WWW SERVER,通过网络最后显示在客户机的屏幕上。
这个例子就很好的体现了CGI在网络中的应用原理,一般都分为"上传"、"处理"、"下传"三个步骤。
以下为CGI的应用原理示意图:
12.3 CGI 的基本实现原理
不管通过什么语言编程,都必须解决数据的输入和输出,需要了解以下两个问题:
怎样HTML中取得用户输入的数据和怎样向用户发出处理好的信息。

12.3.1 怎样从HTML中取得用户输入的数据:
在谈怎样在CGI处理程序中得到用户的输入数据前,我们先谈一下怎样制作与CGI程序对应的HTML输入体系的制作.
制作有关与CGI有关的输入体系的 HTML 是用 FORM 语句来设定的,比如说要建立一个用户信息登录CGI程序所需的HTML语句,其中包括姓名、地址、性别(男女单选)、备注等信息,设计完后的页面是这样的:

那么实际的HTML语句为:
<form action="http://202.196.68.240//cgi-bin/cgi.exe" method="POST">
<p>姓名: <input type="text" size="20" name="T1"></p>
<p>地址: <input type="text" size="20" name="T2"></p>
<p>性别: 男<input type="radio" checked name="R1"value="V1">
女<input type="radio" name="R1" value="V2"> </p>
<p> <textarea name="S1" rows="2" cols="49"></textarea></p>
<p> <input type="submit" name="B1" value="Submit"></p>
</form>
从上面我们可以看出,这是一个CGI的输入体系的基本结构:
第一行:FORM初始化语句,form action是指向CGI的超文本路径,method内容是声明数据的类型,FORM 语句的基本语法是∶
< FORM action = URL method = METHOD >【 form text 】</ FORM >
类型说明:
action 参数是一个指向所需的 CGI 程序的超文本跳转路径。
method 参数是当按 submit 按钮时,会告知服务器接受客端要求的处理方式,METHOD 参数的值可以是 POST,或是 GET,建议最好是用 POST,因为这样可使传给服务器的字符串没有长度限制。
另外值得说明的是:文件中允许有两个以上的输入表单,但不允许存在交错。
第二行:表单内各元素的声明:
从第二行以下,每行申请一个表单元素,我们以第二行来说明,这是一个要求用户输入姓名的文本输入框,在行的一开始,使用〈P〉开始一段,并显示"姓名:",随后,有:
<input type="text" size="20" name="T1">
声明这是一个有关输入的元素,它的类型是文字(TEXT),大小是20个字符,名称是"T1"
,这是一个标准的元素语句,它的基本格式是:
<input type="元素类型" 这个元素的其他变量声明>

其中不同的元素有不用的类型,可以为∶
l TEXT∶文、数字的输入框(缺省值)。
l PASSWORD∶与 TEXT 相同,但不管已设定缺省值或在输入字符时,都会显示「 * 」号,常用于输入密码。
l HIDDEN∶可设定一个隐藏的元素,因隐藏并不显示于屏幕,故无法用输入更改初值,可用此方法将固定值送给应用系统当做字串常数使用,可惜这并没有达到保密效果,因浏览器仍然可用 view source 查阅 HTML 文件原貌。
l CHECKBOX∶多选扭,不是 ON 就是 OFF 。
l RADIO∶单选扭。
l SUBMIT∶会把表格输入的字符串打包后送给远端服务器。并会使服务器执行 Action 下的 URL 所指定地址的CGI 程序 。一个表单能有多个 Submit,但只能允许一个 action 指定的CGI程序。若 checkbox与 radio 已被选中时,其 name 值才会被打包,而 text、password、hidden 不管有无内容,其 name 值定会被打包送出。
l RESET∶把表格内所有值还原为缺省值。
l image : 建立一个clickable 图像,当在图像某位置点一下,则除了有与submit 同样效果之外,并会把x y座标(button_name.x与button_name.y)同时送给远端服务器.

此外,还有针对元素的其他类型声明:
l NAME∶定义元素名称,除了 Submit 与 reset 型态不需 name 外,其余一定要有。
l VALUE∶配合不同类型的元素有不同的意义,对 text 或 password 输入框而言,是设定输入框的初值。对 radio 或 checkbox 也是指定选项初值,不过必须同时设 CHECKED 才有效。对 Submit 或 reset 输入型而言,其文字内容将会变成按钮的标签, 若Submit 内有name属性,则其值也会送给服务器。
l CHECKED∶前面已提过只对 radio 或 checkbox 输入型设置缺省选择。
l SIZE∶对 text 或 password 输入框而言,可以定义输入格的大小,若无 size 项时,则缺省为 20。若需要矩形窗格, 则以 SIZE= 行数, 列数 来定义。
l MAXLENGTH∶只对 text 或 password 输入框有效,其定义接受输入字串的最大长度。若无 MAXLENGTH 则缺省值为无限大。

对于一些高级用户,还可以使用一些比较深入的元素,如:
l SELECT 元件:
可允许使用者以下拉式从多选项中执行单选或复选。此多选项列中是以 < SELECT >为起始,每一个选项值以< OPTION >为开头,最后以</SELECT >结束。SELECT 标签一定有 NAME 来定义选项的字段名称才行,
其中尚有一些选择性参数∶
( 1 ) SIZE∶叙述「滚动式列表」 (scrolled List) 窗口内选项的数量,若没有 SIZE 参数时,则缺省值为 1。
( 2 ) MULTIPLE∶不管有无 SIZE 参数一定是「滚动式列表」,并且允许从列表中做多项选项(以 MS 浏览器为例,CTRL 键与鼠标左键同时使用)。即相同字段名称可拥有多个不同值。
( 3 ) OPTION 内可有参数 selected,此表示此选项值为缺省值,在SELECT 元件内允许多个 <OPTION selected> 选项值。
l TEXTAREA 元件:
此文字区域输入元件提供多行输入区,窗口大小决定于 ROWS 与 COLS之值。此项以< TEXTAREA >为起始标签,</ TEXTAREA >为结尾标签,头尾标签间允许设定文字的初始值,以供使用者修改或接受这段初始值。最后内容会与 NAME 所定义的名称成上传。

最后一行:使用</form>结束表单。

从上面的叙述我们知道了怎样制作与CGI连接的HTML,但是用户输入的数据怎样传给CGI程序呢?假设上例中的输入内容被添成了:
姓名:LIYAPING 男 地址:lyp@public.zz.ha.cn edgesoft
那么当用户选择SUBMIT上传时,FORM实际产生了一个这样的超文本地址:
http://202.196.68.240/cgi-in/cgi.exe?T1=LIYAPING&T2=lyp@public.zz.ha.cn&R1=V1&
S1=edgesoft&B1=Submit
这就是说,FORM语句自动将输入的内容连入超文本路径,最后通过超文本路径来传给CGI程序,而CGI程序可以通过一定的语句从环境变量中读出调用它的超文本路径中"?"后面的内容,具体步骤是:
服务器接受客户端要求服务时,会以表单的 Method 类型来决定与 CGI 程序之间的资料传递方式。若 Method = "post",则会以标准输入 (stdin) 方式传送字符串给 CGI 程序,并在 CONTENT_LENGTH 环境变量通知字符串的长度。所以 CGI 程序必需由 stdin 读取长度为 CONTENT_LENGTH 的字符串。此字符串是会被编成以"&"符号区隔的name=value 字串对。其中 name 为输入元素的栏名称,value 为其内容。value 内的空格( space )会被转换为"+";特殊字符会被转成 %xx 十六进制(例如、中文、&;、= ...,这点在写CGI程序时尤其要注意 )。
故 CGI 处理程序第一步在于如何处理输入字符串,并把 value 被转换过的内容恢复原貌,进而建立 name 与 value 间的对应关系(即把值指定给变量)。当表单 Method = "GET"时,会将字符串(同 POST 已被转码)之前加"?"号,并会紧随在 action 的 URL 后面传给服务器,即由于URL 字串内是以"?"作为地址与查询字串之间的分离符号,故您也可以不透过 FORM 型式而利用 hyperlink 方式直接连结使用。所以此时服务器不是透过 stdin 传递表格字符串,而是字符串借环境变量 QUERY_STRING 传送给 CGI 程序。除此之外,CGI 允许在 ACTION 之 URL 内附加所谓的"额外"信息送给服务器,此额外字串不会经主机编码转换,并且会将它放置在环境变量 PATH_INFO 内传给 CGI 程序,一般在实际应用时,CGI 程序若要参考其他路经的文件或程序时,可把"相对路径"字串以额外信息方式放在 PATH_INFO 内,就可从 PATH_TRANSLATED 环境变量取得"绝对路径"的字串。
知道了怎样制作与CGI程序相连的HTML语句后,就不难理解CGI是怎样取得用户输入的资料的。其实CGI 在服务器中最主要的功能,就是提供程序员能设计外在服务程序以供在 Web 服务器与客户机之间,客户瑞程序能很方便的存取远瑞服务器的数据库。这种方式的建立,是以填表格形式最为直接普遍,并透过 CGI 驱动辅助应用程序,以达到客户与服务端交谈的目的。

12.3.2 怎样向用户发出处理好的信息
我们了解了CGI程序怎样获得用户输入的内容,那么怎样向用户输出信息呢?我们可以通过标准输出( stdout )输出到服务器,然后由把这些资料传回给客户瑞使用者。至于回应给服务器的内容如何被主机认知,则完全根据送给服务器内容的第一行表头 (header) 的内容来决定文件类型,表头行务必空一行以上空白行(即需两个"/n/n" ),再来才是资料的真正内容。常用表头内容格式如下∶
(a)、Content-type∶type/subtype,其中 type/subtype 是指 MIME(Multipurpose Internet Mail Extensions )内容状态。例如一般最常用的是 HTML 文件,其内容状态为 "context-type∶text/html/n/n",此种传输方式可创出所谓的「虚拟 HTML 文件」( Virtual HTML 元件),做为服务器与客户机双向交谈的结构机制。如果表头是"context-type∶ text/plain/n/n",则视为一般 ASC Ⅱ文件输出。
(b)、Location∶告知服务器此输出不是真正文件,而是转向( redirect ),参用其他某地方现有的 HTML 文件或其他服务的主机(例如 gopher )环境。
由此可知,经服务器从数据库读取数据送回浏览器显示前,需促使 CGI 程序先转换成适当HTML 超元件之表示式与连结关系。

12.4 CGI 的 环境变量
在这一小节所列的环境变量仅供参考 ,并不见得会适用于每一个WWW Server。所以,您在使用这些环境变量时,请先阅读过您的 WWW Server 手册,以免产生不必要的错误 !
下列环境变量不是用在特别需求上 ,而是用在所有的需求上:

l GATEWAY_INTERFACE:此变量是用来传送 CGI 的版本号给 Server。格式:CGI/revision
l SERVER_NAME:此变量是用来传送 Server 的名字,Domain Name Server,或 IP 地址。
l SERVER_SOFTWARE:此变量是用来传送 Server 的软件名字和版本。格式∶name/version

下面的环境变量是给 CGI 程序专用 ∶

l AUTH_TYPE:如果Server 提供使用者查核 ,而且程序是有保护的 。 这是一种辨认使用者身份方法的一种协定。该授权标题是用HTTP请求时WEB服务器接受到的。
l CONTENT_LENGTH:这是使用者输入数据的长度。
l CONTENT_TYPE:这是使用者输入数据的MIME格式 。如果提交CGI如果提交CGI脚本的数据是用GET 方法提交的,这个变量将包含application/x-www-form-urlencoded。者表示表单的相应是按URL规范编码的。
l PATH_INFO:此变量是用来传送来自客户端额外的路径,换句话说 ,程序可以藉着真实的路径,加上路径的额外字串来计算出绝对路径 。这额外字串传给PATH_INFO。如果它的URL必须先经过CGI 程序时 ,则要由 Server 解码 。
l PATH_TRANSLATED:此变量是由Server 提供PATH_INFO 的翻译版本 。
l QUERY_STRING: 此变量是传送使用者输入的资料。在任何的版本下不会被解码。只要有使用者输入的资料,此变量就会被设定,不管命令列的解码 。
l REMOTE_ADDR:产生请求的远方客户机的IP 地址。 但这个地址并不是很精确,当对方的浏览器使用代理服务器时,这个变量只能取得代理服务器的IP地址。
l REMOTE_HOST:产生请求的远方客户机名字。如果Server 没有此信息 ,则设定REMOTE_ADDR,并且不设定此变量 。
l REMOTE_IDENT:如果Server提供 RFC 931的认证,则这个变量会从远端的Server取得使用者名字。这个变量应只使用在限制使用者登录上 。
l REMOTE_USER:如果Server提供使用者查核,而且程序是有保护的 ,此变量是用来传送想要通过认证的使用者名字 。
l REQUEST_METHOD:此变量是用来传送使用那一种方法传输资料,以超文本传输协定 (HTTP) 而言是"GET" 、"POST"以及"HEAD"三种方法 。
l SCRIPT_NAME:被执行的程序实质路径即代表自己的URL 。
l SERVER_PORT:此变量是用来传送那一个端口是用来传输要求,即WWW SERVER的端口 。
l SERVER_PROTOCOL:此变量是用来传送协议的名字和版本 。格式 protocol/revision

除此之外,如果有一行以上的档头 (HEADER) 从客户端接收到 ,则会被 WWW Server 在文件头名称前面加上"HTTP_" 的开头之后被当成环境变量 。在文件头中如果有任何 "-" 字符,将会被转成"_"字符。另外Server 会除去任何已处理的开头,像Authorization、content-type 、 content-length 。如果放入这些文件头会超过系统环境限制 , Server 会选择去删除全部或一些开头 。

 
多谢李兄的热心指教,
唯一美中不足的是少了一点源程序.
让我们再等等pegasus在作最后决定
 
Hehe, I have no time to write the demo program now.

I'm sorry I can't input chinese now. (For I'm testing NT5.0 beta 2 now)
But I think you can write yourself, after reading the above detailed
explanation Li Yaping had attibuteed.

I give the pesuado code here:

1. apply socket for network communicationInitiate the socket to 2.connect to the server address and port;
3.Getuser input and compose the string that you will transfer to cgi
4. Construct the request header ("Like Get mothod or PUT method,
content-length, etc."
5. Transfer your request to the webserver.
6. read the result that webserver return, then show to the user.

Anyway, the important is you "Do-and-Test", then you will learn much!

BTW, I have many online book for internet programming and/or
internet standard documents. If you like to learn, please give me a
msg.

Good Luck!
 
多人接受答案了。
 
12.6 怎样使用DELPHI编制CGI程序
DELPHI 3.0 虽然提供了编写CGI程序的向导程序,但并没有多大用处,应为CGI太象DOS程序,无法进行"可视"编程,一切均靠手动.
首先,DELPHI会建立工程文件 DPR 文件,在我们这个例子中,我们用CGI程序来判断FORM内的各元件传入的内容:
下面是CGI.DPR文件内容:
program Mil;

uses
sysUtils,
Pcgi in 'Pcgi.pas';

var
cgi:TCgi;
idx:integer;
ParameterName,ParameterValue:string;

begin
cgi:=TCgi.Create;
writeln('Content-type: text/html');
writeln;
writeln('<title>CGI Test Program</title>');
writeln('<body BACKGROUND="BACK.GIF">');
writeln('<h1>CGI Reply</h1>');
writeln('User Input String:'+cgi.ParamRawData+'<br>');
writeln('Parameter Count:'+IntToStr(cgi.Params.Count)+'<br>');
writeln('<HR>');
for idx:= 1 to cgi.Params.Count do
begin
ParameterName:=cgi.Params.Names[idx-1];
writeln(IntToStr(idx)+' Parameter Name = '+ParameterName+'<br>');
ParameterValue:=cgi.Params.values[ParameterName];
writeln(IntToStr(idx)+' Parameter Value = '+ParameterValue+'<br>');
writeln('<HR>');
end;

writeln('</body>');
cgi.Free;
end.

使用面向对象的编程使程序更加简洁,可以看出这个程序的第一步:
cgi:=TCgi.Create;
这个事件将产生各种初始化处理工作,生成各种我们需要的数据,即各种处理好的环境变量,紧接着,程序使用writeln 语句写入CGI输出的HTML文件头,最后输出详细内容:FORM中全部元件的内容。
当然上程序是不能单独编译的,必须带上程序的核心部分:PCGI.PAS 文件,下面是这个文件的程序清单:

unit Pcgi;

interface

uses
Windows,SysUtils,Classes;


const
DefaultBufferSize=4096;
type

TCgi = Class(TObject)
private
{Private Part Declare}
FInputRawData:String;
function ParamCount:Integer;
function GetToken(aString, SepChar: String; TokenNum: Byte):String;
function NumToken(aString, SepChar: String):integer;
function GetParamItemValue(inpStr:string;Cnt:integer):string;
function GetParamItemName(inpStr:string;Cnt:integer):string;
public
{Public Part Declare}
ContentLength:integer;
ContentType:string;
GatewayInterface:string;
HttpAccept:string;
HttpUserAgent:string;
Params:TStrings;
QueryString:string;
RemoteAddr:string;
RemoteHost:string;
RemoteUser:string;
RequestMethod:string;
ScriptName:string;
ServerName:string;
ServerProtocol:string;
ServerSoftware:string;
constructor Create;
destructor destroy ; override ;
function ParamRawData:string;
end;

implementation

constructor TCgi.Create;
var
buf:pChar;
ContentLengthStr:string;
ret,idx:integer;
begin
inherited Create;
Params:=TStringList.Create;

// Get Server Variable
try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('REQUEST_METHOD',Buf,DefaultBufferSize);
RequestMethod:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('CONTENT_LENGTH',Buf,DefaultBufferSize);
try
ContentLength:=StrToInt(StrPas(Buf));
except
ContentLength:=0;
end;
finally
freeMem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('SERVER_SOFTWARE',Buf,DefaultBufferSize);
ServerSoftware:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('SERVER_NAME',Buf,DefaultBufferSize);
ServerName:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('GATEWAY_INTERFACE',Buf,DefaultBufferSize);
GatewayInterface:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('SERVER_PROTOCOL',Buf,DefaultBufferSize);
ServerProtocol:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('HTTP_ACCEPT',Buf,DefaultBufferSize);
HttpAccept:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('SCRIPT_NAME',Buf,DefaultBufferSize);
ScriptName:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('REMOTE_ADDR',Buf,DefaultBufferSize);
RemoteAddr:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('REMOTE_HOST',Buf,DefaultBufferSize);
RemoteHost:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('REMOTE_USER',Buf,DefaultBufferSize);
RemoteUser:=StrPas(Buf);
finally
Freemem(Buf);
end;

try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('CONTENT_TYPE',Buf,DefaultBufferSize);
ContentType:=StrPas(Buf);
finally
Freemem(Buf);
end;

if Uppercase(RequestMethod)='POST' then
read(FInputRawData)
else
begin
// Get Request Method
try
GetMem(Buf,DefaultBufferSize);
ret:=GetEnvironmentVariable('QUERY_STRING',Buf,DefaultBufferSize);
FInputRawData:=StrPas(Buf);
finally
Freemem(Buf);
end;
end;

for idx:= 1 to ParamCount do
Params.Add(GetParamItemName(FInputRawData,idx)+'='+GetParamItemValue(FInputRawData,idx));

end;

destructor TCgi.destroy;
begin
Params.Free;
inherited Destroy;
end;

function TCgi.ParamRawData:string;
begin
Result:=FInputRawData;
end;

function TCgi.GetToken(aString, SepChar: String; TokenNum: Byte):String;
var
Token : String;
StrLen : Byte;
TNum : Byte;
TEnd : Byte;

begin
StrLen := Length(aString);
TNum := 1;
TEnd := StrLen;
while ((TNum <= TokenNum) and (TEnd <> 0)) do
begin
TEnd := Pos(SepChar,aString);
if TEnd <> 0 then
begin
Token := Copy(aString,1,TEnd-1);
Delete(aString,1,TEnd);
Inc(TNum);
end
else
begin
Token := aString;
end;
end;
if TNum >= TokenNum then
begin
GetToken := Token;
end
else
begin
GetToken := '';
end;
end;

function TCgi.NumToken(aString, SepChar: String):integer;
{
parameters: aString : the complete string
SepChar : a single character used as separator
between the substrings
result : the number of substrings
}

var
RChar : Char;
StrLen : integer;
TNum : integer;
TEnd : integer;

begin
if SepChar = '#' then
begin
RChar := '*'
end
else
begin
RChar := '#'
end;
StrLen := Length(aString);
TNum := 0;
TEnd := StrLen;
while TEnd <> 0 do
begin
Inc(TNum);
TEnd := Pos(SepChar,aString);
if TEnd <> 0 then
begin
aString[TEnd] := RChar;
end;
end;
NumToken := TNum;
end;

function TCgi.GetParamItemName(inpStr:string;Cnt:integer):string;
var
tmpStr:string;
begin
tmpStr:= GetToken(inpStr,'&',Cnt);
result:=GetToken(tmpStr,'=',1);
end;

function TCgi.GetParamItemValue(inpStr:string;Cnt:integer):string;
var
idx:integer;
tmpStr,OutStr:string;
begin
tmpStr:= GetToken(inpStr,'&',Cnt);
tmpStr:= GetToken(tmpStr,'=',2);
idx:=1;
while (idx<=length(tmpStr)) do
begin
if tmpStr[idx]='%' then
begin
OutStr:=OutStr+ chr(StrToint('$'+Copy(tmpStr,idx+1,2)));
inc(idx,3);
end
else
begin
if tmpStr[idx]='+' then
OutStr:=OutStr+chr(32)
else
OutStr:=OutStr+tmpStr[idx];
inc(idx);
end
end;
result:=OutStr;
end;

function Tcgi.ParamCount:Integer;
begin
Result:=NumToken(FInputRawData,'&');
end;

end.


12.7 使用实例
我们就以前面提到的这个简单的例子来说明吧!
例: 建立一个站点,其中需要用户输入用户名称和密码已确认是否是会员,如果是会员,就显示"欢迎进入"的页面,否则显示"谢谢使用,请先注册"。为了说明问题,这里的会员数据库部分暂时省略,假使只有个用户LYP,密码是abcd.
第一步:编写CGI程序,将 CGI.DPR 改成以下内容:
program Mil;

uses
sysUtils,
Pcgi in 'Pcgi.pas';

var
cgi:TCgi;
idx:integer;
ParameterName,ParameterValue:string;

begin
cgi:=TCgi.Create;
writeln('Content-type: text/html');
writeln;
writeln('<title>CGI Test Program</title>');
writeln('<HR>');
if ((cgi.Params.values[cgi.Params.Names[0]]='lyp') and
(cgi.Params.values[cgi.Params.Names[1]]='abcd')) then
writeln('欢迎进入!')
else
writeln('谢谢使用,请先注册!');
writeln('<HR>');

writeln('User Input String:'+cgi.ParamRawData+'<br>');
writeln('Parameter Count:'+IntToStr(cgi.Params.Count)+'<br>');

for idx:= 1 to cgi.Params.Count do
begin
ParameterName:=cgi.Params.Names[idx-1];
writeln(IntToStr(idx)+' Parameter Name = '+ParameterName+'<br>');
ParameterValue:=cgi.Params.values[ParameterName];
writeln(IntToStr(idx)+' Parameter Value = '+ParameterValue+'<br>');
writeln('<HR>');
end;

writeln('</body>');
cgi.Free;
end.

上面这个程序和PCGI.PAS一块编译可以得到CGI.EXE这个文件,将这个文件拷入WWW服务器的CGI-BIN目录下.
第二步:建立HTML
建立一个内容如下的HTML: (目录为:HTTP://202.196.64.240/a/index.htm )
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>

<head>
<meta http-equiv="Content-Type"
content="text/html; charset=gb_2312-80">
<meta name="GENERATOR" content="Microsoft FrontPage 2.0">
<title>CGI实例</title>
</head>

<body>

<p align="center"><font color="#400040" size="7"
face="楷体_GB2312">CGI实例</font></p>

<form action="http://202.196.64.240/cgi-bin/cgi.exe"
method="POST">
<p align="left"><font color="#400040">请输入用户名:<input
type="text" size="20" name="T1"></font></p>
<p align="left"><font color="#400040">请输入口令 : <input
type="password" size="20" name="T2"></font></p>
<p align="left"><font color="#400040"><input type="submit"
name="B1" value="Submit"></font></p>
</form>

<p align="center"><font color="#400040"></font> </p>
</body>
</html>

文中的 <form action="http://202.196.64.240/cgi-bin/cgi.exe"一句中为CGI程序所处的位置,读者可以自行改变.
第三步:运行、试验
首先,我们使用浏览器调入该HTML页面,如图12-7-1
图12-7-1
此时输入正确的用户名和口令,得到图12-7-2:
图12-7-2
如果输出其他的用户名和口令,得到图12-7-3:
图12-7-3

为了方便大家研究,我们还特地留有元件的状态值显示。

 
顶部