url译码(url encoding)就是将web服务器所不能正确处理的特殊字符转换成它的十六进制数的形式,比如将“%”转换成“%25”、“=”转换成“%3d”等等。这些特殊的字符通常被称作web系统的保留字符。在web系统上无论是用get方法还是用post方法传送的数据都要进行url译码。cgi程序要想处理表单传送来的数据,还必须对浏览器url译码过的数据进行解码。因此,理解url译码对于我们进行cgi编程是非常重要的。url译码一般包括以下步骤:
1、浏览器将所传送的数据根据表单所包含的元素分解成“name=value”形式,name和value分别是表单元素的属性。其中,value属性中存储客户机在表单中输入的数据:如果客户机没有输入数据,则value存储的是表单定义的缺省值;如果缺省值也没有定义,则value值为空。
2、代表表单中各元素的各个“name=value”对被浏览器用“&”连接起来。
3、value属性中存放的数据若含有空格,则被转换成“+”。
4、url和输入数据中所包含的web系统的保留字符必须被译码成其十六进制数形式。
5、被译码后的字符被表示成一个“%”和它们的十六进制数形式(即%hh)。
cgi程序从环境变量“query_string”或标准输入中读入的数据是经过浏览器url译码过的,故在使用这些数据以前还必须对它们进行url解码。解码的目的是将数据还原成客户端用户在web页面上输入时的形式。本文已经介绍了url译码过程,url解码过程与它正好相反,它一般包括以下步骤:
1、从浏览器用get或post方法所传送来的数据中找出代表各个表单元素所储存数据的“name=value”对。
2、value属性中所存放的数据若含有“+”,则被转换成空格。
3、将value属性中所存放的数据的十六进制数“%hh”转换成相应的字符。
web系统将汉字当成特殊的字符,对它也要进行url译码。对于一个特殊的单字节字符(比如“/”),浏览器通常将它译码成十六进制数的形式(比如%2f),“%”表示它后面跟的是两位十六进制数。当vb程序对其进行处理时调用chr$函数就可以将其恢复为原貌。而一个汉字则被浏览器译码成四位十六进制数(比如%d5%c5)。如果cgi程序还像以前那样分别调用chr(d5)和chr(c5),则由于d5、c5都不是正常的单字节十六进制数码,故chr函数返回空,汉字将无法正确还原。正确的做法应该是将有关汉字的四位十六进制数一起传给函数chr(如chr(d5c5)),此时汉字才能被正确还原。
因此,可以让cgi程序对四位连续的十六进制数一起进行译码,以便使汉字能够被正确还原。但在这种情况下,当客户端用户输入了两个连续的web系统保留字符时,cgi程序又可能把它们当成汉字来处理。这时可以让cgi程序在需要对四位连续的十六进制数进行译码时首先检查前面两位是否为web系统的保留字符,如果是则仍然按照单字节的字符处理。不过如果客户端用户在表单内填写了很多汉字,则cgi程序的负担将会大大加重。事实上,在大多数情况下,客户端用户很少会使用两个连续的web系统的保留字符,所以可以只让cgi程序对最容易出现的情形如“://”(当客户端用户在表单中输入某一url时会出现这种情况)进行检查,本文下节提供的函数urldecode( )可以实现对汉字和web系统保留字符的url解码。
三、cgi编程实例
本节将用vb编写一个处理主页客户留言簿的cgi程序。除了要调用本文前面所介绍的win32api函数外,程序中还调用了win32api函数gettempfilename()来获得一个唯一的临时文件名。程序中的函数urldecode()用来对客户端的输入进行url译码。函数getcgivalue()则用来分解字符串,根据表单元素的name属性获取其value值,并调用urldecode()函数对其进行url译码。
本程序要求在留言簿文件guests.html中使用一个定位串“<! endhead >”,将文件的开始部分和具体的客户留言部分分开。cgi程序将在“<! endhead >”所在的位置插入客户新的留言。guests.html应具有如下所示的样式:
<html>
<head><title>dhtml zone </title></head>
<body bgcolor="#ffffff" text="#00000" vlink="#990000" link="#333399">
<! endhead >
<!---客户的留言部分从这开始-->
<p>……………………….
<!---客户的留言部分结束于此-->
</body></html>
这种样式将保证最后的留言出现在留言簿的最前面。如果要想使最后的留言出现在留言簿的最后面,则只需将留言簿文件中的定位字符串“<! endhead >”移到留言簿文件中客户留言部分和html文件结尾部分之间的位置就行了。
整个程序的完整代码如下所示:
'guestbook.bas
declare function getstdhandle lib "kernel32" (byval nstdhandle as long) as long
declare function readfile lib "kernel32" (byval hfile as long, lpbuffer as any,byval nnumberofbytestoread as long, lpnumberofbytesread as long, lpoverlapped as any) 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
declare function gettempfilename lib "kernel32" alias "gettempfilenamea"(byval lpszpath as string, byval lpprefixstring as string, byval wunique as long, byval lptempfilename as string) as long
public const std_input_handle = -10&
public const std_output_handle = -11&
public const file_begin = 0&
public hstdin as long ' 标准输入文件句柄
public hstdout as long ' 标准输出文件句柄
public sformdata as string ' 用于存储没有经过url译码的用户输入数据
public lcontentlength as long
public cgi_requestmethod as string
sub main()
dim cgi_contentlength as string, cgi_querystring as string, sbuff as string, chinesetail as string
dim lbytesread as long, rc as long,i as long
dim semail as string, sname as string, surl as string, sfrom as string, tempstring as string
dim scomment as string, tempfilename as string, guestbook as string
'cgi程序的初始化工作
hstdin = getstdhandle(std_input_handle)
hstdout = getstdhandle(std_output_handle)
cgi_requestmethod = environ("request_method")
cgi_querystring = environ("query_string")
cgi_contentlength = environ("content_length")
lcontentlength = val(cgi_contentlength)
sbuff = string(lcontentlength, chr$(0))
output "content-type: text/html" & vbcrlf ' 输出mime类型
output "<font size=""+2"">"
if cgi_requestmethod = "post" then
sbuff = string(lcontentlength, chr$(0))
rc = readfile(hstdin, byval sbuff, lcontentlength, lbytesread, byval 0&)
sformdata = left$(sbuff, lbytesread)
elseif cgi_requestmethod = "get" then
sformdata = cgi_querystring
else
output "unknow form method !"
end if
'为了在页面上正确显示中文,生成一个空格串以获取客户端用户的输入
chinesetail = string(400, " ")
sname = getcgivalue("name")
semail = getcgivalue("email")
surl = getcgivalue("url")
sfrom = getcgivalue("from")
scomment = getcgivalue("url_comment")
'对客户端用户的输入进行检查
if len(sname) = 0 then
output "<p>非常抱歉!您还没有填写姓名!" & chinesetail
exit sub