三、CGI編程實例
本節將用VB編寫一個處理主頁客戶留言簿的CGI程序。除了要調用本文前面所介紹的Win32API函數外,程序中還調用了Win32API函數GetTempFileName()來獲得一個唯一的臨時文件名。程序中的函數UrlDecode()用來對客戶端的輸入進行URL譯碼。函數GetCgiValue()則用來分解字符串,根據表單元素的NAME屬性獲取其VALUE值,并調用UrlDecode()函數對其進行URL譯碼。
本程序要求在留言簿文件guests.html中使用一個定位串“$#@60;! ENDHEAD $#@62;”,將文件的開始部分和具體的客戶留言部分分開。CGI程序將在“$#@60;! ENDHEAD $#@62;”所在的位置插入客戶新的留言。guests.html應具有如下所示的樣式:
$#@60;html$#@62;
$#@60;head$#@62;$#@60;title$#@62;DHTML Zone $#@60;/title$#@62;$#@60;/head$#@62;
$#@60;body bgcolor="#FFFFFF" text="#00000" vlink="#990000" link="#333399"$#@62;
$#@60;! ENDHEAD $#@62;
$#@60;!---客戶的留言部分從這開始--$#@62;
$#@60;P$#@62;……………………….
$#@60;!---客戶的留言部分結束于此--$#@62;
$#@60;/body$#@62;$#@60;/html$#@62;
這種樣式將保證最后的留言出現在留言簿的最前面。如果要想使最后的留言出現在留言簿的最后面,則只需將留言簿文件中的定位字符串“$#@60;! ENDHEAD $#@62;”移到留言簿文件中客戶留言部分和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 ST NPUT_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 "$#@60;FONT SIZE=""+2""$#@62;"
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 "$#@60;P$#@62;非常抱歉!您還沒有填寫姓名!" & chinesetail
Exit Sub
End If
If Len(sComment) = 0 Then
OutPut "$#@60;P$#@62;非常抱歉!您還沒有提出建議!" & chinesetail
Exit Sub
End If
獲取唯一的臨時文件名和留言簿文件并打開它們
tempFileName = TempFile("c:windowsemp", "gbk")
guestbook = "e:etscapeserverdocsguests.html"
Open tempFileName For Output As #1
Open guestbook For Input As #2
Do 本循環體用于將留言簿中字符串"$#@60;! ENDHEAD $#@62;"前面的內容寫入臨時文件
Line Input #2, tempstring
Print #1, tempstring
Loop While tempstring $#@60;$#@62; "$#@60;! ENDHEAD $#@62;" And Not EOF(2)
向臨時文件中插入客戶端用戶的留言
Print #1, "$#@60;hr$#@62;" & vbCrLf
Print #1, "$#@60;ul$#@62;" & vbCrLf
Print #1, "$#@60;li$#@62;$#@60;b$#@62;留言時間$#@60;/b$#@62;:" & Date$ & " " & Time$ & vbCrLf
Print #1, "$#@60;li$#@62;$#@60;b$#@62;姓名: $#@60;/b$#@62;" & sName & vbCrLf
If Len(sEmail) $#@60;$#@62; 0 Then
Print #1, "$#@60;li$#@62;$#@60;b$#@62;E-mail: $#@60;/b$#@62;$#@60;a href=""mailto:" & sEmail & """ $#@62;" & sEmail & "$#@60;/a$#@62;" & vbCrLf
End If
If Len(sURL) $#@60;$#@62; 0 Then
Print #1, "$#@60;li$#@62;$#@60;b$#@62;我的主頁: $#@60;/b$#@62; $#@60;a href=""" & sURL & """ $#@62;" & sURL & "$#@60;/a$#@62;" & vbCrLf
End If
If Len(sfrom) $#@60;$#@62; 0 Then
Print #1, "$#@60;li$#@62;$#@60;b$#@62;我來自: $#@60;/b$#@62;" & sfrom & vbCrLf
End If
Print #1, "$#@60;li$#@62;$#@60;b$#@62;我的建議: $#@60;/b$#@62;" & vbCrLf
Print #1, sComment & vbCrLf
Print #1, "$#@60;/ul$#@62;" & vbCrLf
Do 本循環體用于將留言簿剩余的東西寫入留言簿
Line Input #2, tempstring
Print #1, tempstring
Loop While Not EOF(2)
Close #1
Close #2
Kill guestbook 刪除舊的留言簿
Name tempFileName As guestbook 將臨時文件改成新的留言簿
OutPut "$#@60;P$#@62;非常感謝您的留言!" & chinesetail
OutPut "$#@60;P$#@62;歡迎您經常光顧本主頁!" & chinesetail
OutPut "$#@60;/FONT$#@62;"
End Sub
Sub OutPut(s As String) 本子程序用于向標準輸出寫信息
Dim lBytesWritten As Long
s = s & vbCrLf
WriteFile hStdOut, s, Len(s), lBytesWritten, ByVal 0&
End Sub
Public Function GetCgiValue(cgiName As String) As String
本子程序可以獲取表單上某一元素的數據
Dim delim2 As Long position of "="
Dim delim1 As Long position of "&"
Dim n As Integer
Dim pointer1 As Long,pointer2 As Long,length As Long,length1 As Long
Dim tmpstring1 As String,tmpstring2 As String
pointer1 = 1
pointer2 = 1
delim2 = InStr(pointer2, sFormData, "=")
pointer2 = delim2 + 1
Do
length = delim2 - pointer1
tmpstring1 = Mid(sFormData, pointer1, length)
delim1 = InStr(pointer1, sFormData, "&")
pointer1 = delim1 + 1
length1 = delim1 - pointer2
If delim1 = 0 Then length1 = lContentLength + 1 - pointer2
If tmpstring1 = cgiName Then
tmpstring2 = Mid$(sFormData, pointer2, length1)
GetCgiValue = UrlDecode(tmpstring2)
Exit Do
End If
If delim1 = 0 Then
Exit Do
End If
delim2 = InStr(pointer2, sFormData, "=")
pointer2 = delim2 + 1
Loop
End Function
Public Function UrlDecode(ByVal sEncoded As String) As String
本函數可以對用戶輸入的數據進行URL解碼
Dim pointer As Long sEncoded position pointer
Dim pos As Long position of InStr target
Dim temp As String
If sEncoded = "" Then Exit Function
pointer = 1
Do 本循環體用于將"+"轉換成空格
pos = InStr(pointer, sEncoded, "+")
If pos = 0 Then Exit Do
Mid$(sEncoded, pos, 1) = " "
pointer = pos + 1
Loop
pointer = 1
Do
本循環體用于將%XX轉換成字符。對于兩個連續的%XX,如果第一個%XX不是某些特指的Web系統保留字符,將把它們轉換成漢字
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 $#@60;$#@62; ":") And (temp $#@60;$#@62; "/") _
And (temp $#@60;$#@62; "(") And (temp $#@60;$#@62; ")") And (temp $#@60;$#@62; ".") And (temp $#@60;$#@62; ",") _
And (temp $#@60;$#@62; ";") And (temp $#@60;$#@62; "%") 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
Exit Function
End Function
Public Function TempFile(sPath As String, sPrefix As String) As String
本函數可以獲得一個唯一的臨時文件名
Dim x As Long,rc As Long
TempFile = String(127, Chr$(0))
rc = GetTempFileName(sPath, sPrefix, ByVal 0&, TempFile)
x = InStr(TempFile, Chr$(0))
If x $#@62; 0 Then TempFile = Left$(TempFile, x - 1)
End Function
CGI程序guestbook.bas所要處理的表單如下所示:
$#@60;html$#@62;$#@60;head$#@62;$#@60;title$#@62;貴賓留言簿$#@60;/title$#@62;$#@60;/head$#@62;
$#@60;body$#@62;
$#@60;h3$#@62;貴賓留言簿測試$#@60;/h3$#@62;
$#@60;form action="/cgi-bin/guest.exe" method="post"$#@62;
您的姓名: $#@60;input type="text" name="name"$#@62;$#@60;br$#@62;
您的Email信箱: $#@60;input type="text" name="email"$#@62;$#@60;br$#@62;
您的主頁的URL: $#@60;input type="text" name="URL"$#@62;$#@60;br$#@62;
您的建議:$#@60;br$#@62; $#@60;textarea name="URL_Comment" rows=4 cols=30$#@62;$#@60;/textarea$#@62;$#@60;br$#@62;
您來自: $#@60;input type="text" name="from"$#@62;$#@60;br$#@62;
$#@60;input type="submit" value=" 留言 "$#@62;
$#@60;/form$#@62;
$#@60;/body$#@62;$#@60;/html$#@62;
雖然目前已經有很多可以取代CGI且其性能較CGI要高的技術(例如ASP、ISAPI、NSAPI等),但使用它們時需要用到專門的知識和工具,并且利用這些技術所編制的程序只適用于特定的Web服務器或系統平臺。考慮到CGI編程具有易用易學性、跨服務器平臺特性等優點,因此,CGI程序還將在WWW上占有一席之地。