黑龙江省牡丹江市儿童医院内科 张建华
随着网络的普及,越来越多的网站增设了各种实时交互功能,如网上调查、时事论坛、聊天室、留言板等。下面介绍一个使用VB编写CGI脚本程序的实例,使你轻松拥有自己的留言板。
一、 基本概念
1.什么是CGI
CGI是Common Gate Interface(通用网关接口)的缩写,它运行在Web服务器上,提供同客户端浏览器界面的接口。通过CGI可以使Web服务器执行一些外部程序,并将外部程序所产生的结果返回给Web浏览器。
CGI程序由两部分组成,一部分是超文本页面(html),就是客户端在浏览器中看到的交互式的表单(如调查表和留言板的界面);另一部分是运行在服务器上的程序(通过点击页面上的“提交”按钮激活)。其基本的交互过程如下:
1) Web浏览器在接到客户端对CGI程序的请求后,首先把要传送的数据(客户输入)进行编码,然后提交给服务器;
2) 服务器调用CGI程序,对输入数据进行相关的解码处理,并把处理后的结果写入指定的输出文件;
3) 服务器读取输出文件并把最终数据发送到客户端,使用户可以在浏览器中看到程序执行的结果。
2.数据的提交方式
如果从客户端向服务器传送的数据量不是很多,即小于1024字节,则可以使用“GET”传输方式,命令的使用方法为:在客户端浏览器的交互式表单的源码中加入如下一行代码
<Form method ="GET" action ="/cgi-bin/guest.exe">
当使用这种方法时,CGI程序从环境变量QUERY_STRING中获取数据(即URL的问号“?”之后的那一部分数据)。
如果要通过CGI程序传送的数据超过了1024字节(URL的最大长度限制),例如论坛中的“帖子”,则要使用“POST”的传输方法。
3.浏览器的编码方式
Web浏览器对客户端的输入信息有统一的编码格式:
1) 用“=”连接提交表单中各元素的Name和Value属性,即以“名称=值”的形式进行编码;
2) 用“&”连接表单中不同的输入项目,例如:“Name=王哲&Email=wangzhes@netease.com”;
3) 若Value属性中存放的数据含有空格,则空格被转换成“+”,如“王 哲”被转换为“王+哲”;
4) 对URL和Web系统的保留字符编码成十六进制数形式,即%HH。
CGI程序在使用这些数据前必须对它们进行解码,即将数据还原成用户在Web页面上输入时的形式。由于Web系统将汉字当作特殊字符对待,一个汉字被浏览器编码成四位十六进制数(例如%D5%C5),因此在CGI程序中还要对汉字进行一些特殊的解码处理。
二、编制CGI程序
了解了上述CGI的一些基本特征后,我们就可以着手编制自己的留言板脚本程序了。
1. 页面设置
首先要新建两个超文本(html)文档,可分别命名为guestbook.htm和databook.htm,其中的guestbook.htm为客户在浏览器中看到的交互式表单页面,其源代码如下:
<html><head><title>留言板</title></head>
<body><center><h2>请留下您的建议</h2>
<Form method ="GET" action ="/cgi-bin/guest.exe">
您的名字:<input type =text name =name size=33><br>
您的Email:<input type =text name =email size=32><br>
您的主页地址: <input type =text name =URL size=28 value ="http:// " ><br>
您所在的城市: <input type =text name =city size=27><br><br>
留言内容: <br><textarea name =content COLS=60 ROWS=4></textarea><p>
<input type =submit value="提交"> <input type =reset value="重写">
</form></center> </body></html>
在databook.htm文档中存储的是各位来宾的留言,特殊之处是在文件中要使用一个定位字符串“<! ----- >”将文件的开始部分和具体的客户留言部分分开。CGI程序将在“<! ----- >”所在的位置之后插入客户的留言。databook.htm的源文件为:
<html><head><title>看留言</title></head>
<body text="#00000" vlink="#990099" link="#333399">
<center><h1>查看留言</h1></font></center>
<!----->
<留言插入位置>
</body></html>
这样,最新的留言出现在页面的最上端;如果要把最新的留言放在页面的下端,则只需将其中的定位字符串“<! ----- >”移到客户留言部分和HTML文件结尾部分之间的位置就行了。
需注意的是:<!----->字符串一定要单独占一行。
2. CGI程序
用VB编写的CGI程序的完整代码附后。
三.程序调试
程序编写完成后,要进行调试排错处理。CGI脚本不同于其他VB程序之处在于,CGI程序不能在个人计算机上进行测试,而要放到服务器上的特定目录下才能执行(一般放在Cgi-Bin目录下)。要实现这一点,对网络管理员来说不成什么问题(因为其具有对服务器的读写操作权限),但对于大多数个人计算机爱好者来说却是一个很大的难题。
因此,要对CGI脚本进行测试,首先要在个人PC机上设置一个Web服务器的调试环境,即先在个人PC上运行Web Server软件来模拟服务器。
由于Windows操作平台的局限性,可在Windows下运行的Server软件并不是很多,比较简单且易于实现的一个方法是使用Windows98光盘中自带的Server工具——Personal Web Server(个人网络服务器)。
1.设置个人Web服务器
在Windws98光盘中的“\add-one\pws”目录中,点击“Setup.exe”进行安装。整个安装过程比较简单,按屏幕提示操作即可。
安装完成后,在其主画面左侧点击“高级”图标,在虚拟目录列表框内选择“home”目录,点击“添加”按钮,选择“浏览”,选中c:\Inetpub\wwwroot\cgi-bin(其中的盘符视执行安装时的路径而定),在“新虚拟目录”栏内输入“cgi-bin”,然后在访问权限的设置中把“读取”、“执行”、“脚本”各项全选上,点击“确定”按钮。即成功地设置了个人Web服务器。
2. 测试操作
首先,把我们在前面所新建的两个超文本文档guestbook.htm和databook.htm拷贝到c:\Inetpub\webpub子目录内,把编译完的CGI程序guest.exe拷入c:\Inetpub\wwwroot\cgi-bin子目录内;然后,打开浏览器,在地址栏内输入http://127.0.0.1/guestbook.htm,回车;就可以对CGI脚本程序进行测试操作了。
如果在回车后出现请求“拨号连接”的画面,则先点击“取消”按钮,然后在浏览器的“查看->Internet选项->连接”标签中(以IE为例),选择“通过局域网连接到Internet”一项,再点击“确定”即可。
经测试确认无误后,便可以与自己放置主页站点的网络管理员联系,把编制好的留言板脚本程序上传到服务器上了。
需注意一点,如果网络管理员把你的文档放在与前述目录不同的目录中,则要在脚本中做出相应的修改,并重新编译生成EXE执行文件。
附:guest .bas
声明部分
Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) As Long
Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer _
As String, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten _
As Long, lpOverlapped As Any) As Long
Option Explicit
Public StrData(5) As String 用于存储表单内各项目的用户输入数据
Public CGI_QueryString As String 定义环境变量
Public Const STD_INPUT_HANDLE = -10&
Public Const STD_OUTPUT_HANDLE = -11&
Public hStdIn As Long 标准输入文件句柄
Public hStdOut As Long 标准输出文件句柄
Public Ent As String
主函数
Sub Main()
Dim UrlString As String, Databook As String
Dim TempFileName As String, TempString As String
Dim Number As Single, I As Integer
Ent = vbCrLf
hStdIn = GetStdHandle(STD_INPUT_HANDLE)
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
CGI_QueryString = Environ("QUERY_STRING") 读取返回字符
TempString = TempString + CGI_QueryString + vbCrLf
UrlString = UrlDecode(TempString)
TempString = FenGe(UrlString)
获得一个随机数作为临时文件名
Randomize
Number = Int(10000 * Rnd + 100) 随机得到100到10000之间的整数
TempFileName = "YourWebSitePath1 \" & Number 注①
取得留言簿文件名
Databook = "YourWebSitePath2 \databook.htm" 注②
Open TempFileName for Output As #1
Open Databook For Input As #2
将留言簿中字符串"<!----->"前面的内容写入临时文件
Do
Line Input #2, TempString
Print #1, TempString
Loop While TempString <> "<!----->"
检查客户端用户的输入项
If StrData(1) < " " Then
Send "Content-type:text/html" & Ent
Send "不要着急,您忘记填写名字了! "
Exit Sub
End If
If StrData(5) < " " Then
Send "Content-type:text/html" & Ent
Send "对不起,您还没有填写留言的内容。"
Exit Sub
End If
向临时文件中写入客户端用户的留言
Print #1, "<ul>" & vbCrLf
Print #1, "<li>时间:" & Date & " " & Time
Print #1, "<li>姓名:" & StrData(1)
如果不填写以下各项则不显示该项的名称
If StrData(2) > " " Then Print #1, "<li>E-mail:<small><a href=mailto:" _
& StrData(2) & ">" & StrData(2) & "</a></small>"
If StrData(3) > "http:// " Then Print #1, "<li>主页:<a href=" & _
StrData(3) & " TARGET=_blank >" & StrData(3) & "</a>"
If StrData(4) > " " Then Print #1, "<li>来自:" & StrData(4)
以下循环查找留言内容中的回车符,将其替换为浏览器能够
识别的换行符
Do
I = InStr(StrData(5), Chr(13))
If I = 0 Then Exit Do
UrlString = Mid(StrData(5), 1, I - 1)
StrData(5) = UrlString + "<br>" + Mid(StrData(5), I + 1)
Loop
Print #1, "<li>内容:" & StrData(5) & vbCrLf; "</ul><hr>"
将留言簿中以前的留言内容写入临时文件
Do
Line Input #2, TempString
Print #1, TempString
Loop While Not EOF(2)
关闭打开的文件并将临时文件更名
Close #1
Close #2
Kill Databook
Name TempFileName As Databook
在客户端显示处理结果
Send "Content-type:text/html" & Ent
Send "<HTML><body><center><br>您的建议已成功提交<br>"
Send "<br><a href=\webpub\databook.htm>" 注③
Send "点这里查看结果</a></center></body></html>"
End Sub
该子程序用于处理向客户端返回的数据
Sub Send(s As String)
Dim lBytesWritten As Long
s = s & Ent
WriteFile hStdOut, s, LenB(StrConv(s, vbFromUnicode)), lBytesWritten, ByVal 0&
End Sub
本函数用于对用户输入的数据进行解码
Public Function UrlDecode(ByVal sEncoded As String) As String
Dim pointer As Long, pos As Long
Dim temp As String
If sEncoded = "" Then Exit Function
pointer = 1
Do 该循环对系统保留字和汉字进行处理
pos = InStr(pointer, sEncoded, "%")
If pos = 0 Then Exit Do
temp = Chr("&H" & (Mid(sEncoded, pos + 1, 2)))
If Mid(sEncoded, pos + 3, 1) = "%" And (temp <> ":") _
And (temp <> "\") And (temp <> "/") Then
Mid(sEncoded, pos, 2) = Chr("&H" & (Mid(sEncoded, pos + 1, 2)) _
& (Mid(sEncoded, pos + 4, 2)))
sEncoded = Left(sEncoded, pos) & Mid(sEncoded, pos + 6)
pointer = pos + 1
Else
Mid(sEncoded, pos, 1) = temp
sEncoded = Left(sEncoded, pos) & Mid(sEncoded, pos + 3)
pointer = pos + 1
End If
Loop
UrlDecode = sEncoded
End Function
本函数用于对输入的“Name = Value”数据对进行分解
Public Function FenGe(Cstring As String) As String
Dim I As Integer, j As Integer, add As Integer
j = 1
Do While InStr(Cstring, "&") <> 0
I = InStr(Cstring, "&")
StrData(j) = Mid(Mid(Cstring, 1, I - 1), InStr(Mid(Cstring, 1, I - 1), "=") + 1)
j = j + 1
Cstring = Mid(Cstring, I + 1)
Loop
StrData(j) = Mid(Cstring, InStr(Cstring, "=") + 1)
For I = 1 To j 该循环用于把字符串内的“+”还原为空格
Do
add = InStr(StrData(I), "+")
If add = 0 Then Exit Do
Mid(StrData(I), add, 1) = " "
Loop
Next I
End Function
注①:“TempFileName = "YourWebSitePath1\" & Number”中的YourWebSitePath1是由网络管理员指定的Web服务器上的绝对路径,用于存放临时文件。若是在自己计算机上进行测试,可以先设定为“c:\windows\temp\”。
注②:本句中的YourWebSitePath2亦是由网络管理员分配的服务器路径,用于存放超文本文档。自己测试时,可以先设定为“c:\inetpub\webpub\”。
注③:在上传时,“webpub”目录要随注②中YourWebSitePath2的改变而改变,不要遗忘,否则要出错。