5-1 CGI 蝪∩?

在 Web 伺服器剛開始興起時,其主要功能只是提供檔案和資訊的交流,因此其功能非常有限,只能回傳文字及影像,非常缺乏互動的功能。典型的互動功能,就是接受使用者的表單輸入,並回傳資料處理的結果。由於當時的 Web 伺服器並無內建這種處理使用者表單輸入的功能,因此要達到此互動功能,就要靠外部程式的功能。基本上,此外部程式和 Web 伺服器是獨立的,Web 伺服器以環境變數的方式將使用者的輸入送到此外部程式,外部程式處理後,再將結果回傳伺服器,伺服器再將此資訊回傳給使用者。此種在使用者Web 伺服器外部程式三者之間交換資訊的機制,我們稱之為 Common Gateway Interface,簡稱 CGI。

從程式設計師的角度來看,CGI 就是一組預先定義好的環境變數,Web 伺服器和外部程式就根據這些環境變數,由標準輸入( C語言中的 stdin)與標準輸出( C語言中的 stdout )存取資料。所以一個典型的 CGI程式,依流程可約略分成三部份:

  1. 輸入(從環境變數或標準輸入中讀入資料)
  2. 執行主程式
  3. 輸出(將執行結果至標準輸出)
CGI 的官方網站在伊利諾大學香檳城分校的高速電腦中心,內有 CGI的完整規格:
http://hoohoo.ncsa.uiuc.edu/cgi/

執行 CGI 的外部程式可以是各種不同語言所寫的程式,例如 Perl、C、C++、VB、Pascal、DOS batch files、Unix shell scripts 等等,只要是能符合 CGI 的規範,就可以了。但由於 Perl 對文字處理能力很強,而且在 Unix 社群內早已受到廣大的使用,因此使用 Perl 來進行 CGI 的功能,就變成是自然而然的趨勢了。(請記得在早年時,Web 伺服器只有在 Unix 平台才有,因此 Perl 才順理成章的變成 CGI 的最佳女主角。)

我們首先來看一個最簡單的 Perl CGI 的範例:

Example(hello1.pl):

上述範例的完整原始檔案 (hello1.pl) 如下:

原始檔(hello1.pl):(灰色區域按兩下即可拷貝)
print "Content-type: text/html\n\n";
print "Hello, world!!\n";

在上述範例中,第一列即印出回傳內容的性質,此性質可讓瀏覽器知道如何呈現從外部程式所收到的資料,通成這一列稱為「 “Content-type” line」,而所填入的 Content type 是遵循 MIME(Multipurpose Internet Mail Extension) 的標準,MIME 的相關細節可見http://www.oac.uci.edu/indiv/ehood/MIME/2045/rfc2045.html。在使用 Perl CGI 時,請特別注意:

  1. 一定要印出 "Content-type" line。
  2. 在 "Content-type" line 之後,一定要加上一個空白列。
若你的 Perl CGI 不符合上述兩項規範,伺服器就無法將適當的檔頭資訊送到瀏覽器端,因此伺服器就會回傳一個錯誤的訊息,例如:

Example(helloCgiError.pl):

上述範例的完整原始檔案 (helloCgiError.pl) 如下:

原始檔(helloCgiError.pl):(灰色區域按兩下即可拷貝)
print "Hello World!";

很明顯的,由於缺少了宣告後續資訊的內容,因此導致伺服器只能回傳錯誤訊息。

我們也可以使用 Perl 的 Here document 來一次印出所有回傳的內容:

Example(hello2.pl):

上述範例的完整原始檔案 (hello2.pl) 如下:

原始檔(hello2.pl):(灰色區域按兩下即可拷貝)
print <<END_OF_HTML;
Content-type: text/html

<html>
<head>
</head>
<body>
<hr>
<h3 align=center>Hello, World!!</h3>
<h3 align=center>This is a page returned by Perl's "here document"!</h3>
<hr>
</body>
</html>
END_OF_HTML

若要回傳純文字的訊息,"Content-type" line 就要隨之更改,例如:

Content-type: text/plain

這是回傳的純文字訊息...
也可以直接回傳二進制資料,例如若要回傳 gif 影像資料,範例如下:
Content-type: image/gif

GIF89a&%--- binary contents of GIF file here ---
$(*&%(*@#...... 
Perl CGI 也有「轉址」的功能,只需要再回傳資訊的第一列寫出轉址位置,再加上一個空白列,就可以了,例如:

Example(redirect.pl):

上述範例的完整原始檔案 (redirect.pl) 如下:

原始檔(redirect.pl):(灰色區域按兩下即可拷貝)
print "Location: http://neural.cs.nthu.edu.tw/jang\n\n";

特別提醒:一定要列印出一個空白列,轉址功能才能發揮,否則同樣會造成伺服器的內部錯誤。

以下範例會從指定的數個影像檔案中,隨機挑出一個:

Example(randomImage0.pl):

上述範例的完整原始檔案 (randomImage0.pl) 如下:

原始檔(randomImage0.pl):(灰色區域按兩下即可拷貝)
#! /usr/local/bin/perl

$base_url = "http://neural.cs.nthu.edu.tw/jang/graphics/background/";
@images = ("tile.gif", "back.gif", "yellow.gif", "rain.gif", "marble.gif");
srand;				# Set random seed
$num = rand(@images);		# Pick a random Number
print "Location: $base_url$images[$num]\n\n"; # Print out the header

我們也可以指定一個目錄,讓程式碼直接由目錄中隨機挑出一個檔案:

Example(randomImage1.pl):

上述範例的完整原始檔案 (randomImage1.pl) 如下:

原始檔(randomImage1.pl):(灰色區域按兩下即可拷貝)
$baseUrl = "http://neural.cs.nthu.edu.tw/jang/graphics/background/";
$imageDir = "d:/users/jang/graphics/background";

opendir(DIR, $imageDir);
@files = sort readdir(DIR);
shift(@files);			# get rid of "."
shift(@files);			# get rid of ".."
srand;				# Set a random seed
$num = rand(scalar @files);		# Pick a Random Number
print "Location: $baseUrl$files[$num]\n\n";	# Print Out Header With Random Filename and Base Directory

應用此概念,我們也可以產生一個連結,每次點選時,即從特定目錄隨機選取一首 Midi 檔案來播放,請試試此連結,其程式碼如下:

原始檔(randomMidi1.pl):(灰色區域按兩下即可拷貝)
$baseUrl = "http://neural.cs.nthu.edu.tw/jang/audio/midi/";
$midiDir = "d:/users/jang/audio/midi";

opendir(DIR, $midiDir);
@files = sort readdir(DIR);
shift(@files);			# get rid of "."
shift(@files);			# get rid of ".."
srand;				# Set a random seed
$num = rand(scalar @files);		# Pick a Random Number
print "Location: $baseUrl$files[$num]\n\n";	# Print Out Header With Random Filename and Base Directory

綜合上述範例,我們可以產生一個網頁,能夠隨機產生背景圖片及音樂:

Example(randomAll.asp):

上述範例的完整原始檔案 (randomAll.asp) 如下:

原始檔(randomAll.asp):(灰色區域按兩下即可拷貝)
<% title="由 Perl 產生的隨機背景圖片與音樂" %>
<html>
<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=big5">
<title><%=title%></title>
<embed src="randomMidi1.pl" hidden=true autostart=true loop=false>
</head>
<body background="randomImage1.pl">
<h2 align=center><%=title%></h2>
<hr>
<center>按「<A href="javascript:history.go(0)">重新整理</A>」,即可隨機產生新的背景圖片和音樂。</center>
<!--#include file="foot.inc"-->

此外,Perl 在執行 CGI 功能時,會有許多相關的環境變數,這些環境變數相當重要,可見下列範例:

Example(listEnvVar.pl?opt1=abc&opt2=xyz):

完整的原始 Perl 檔案 (listEnvVar.pl?opt1=abc&opt2=xyz) 如下:

原始檔(listEnvVar.pl?opt1=abc&opt2=xyz):(灰色區域按兩下即可拷貝)
print "Content-type: text/html\n\n";

print "<HTML>\n";
print "<HEAD><TITLE>Environmental Variables Checkup</TITLE></HEAD>\n";
print "<body>\n";
print "<h2>Env. Variables Observed by $0</h2>\n";
print "<b><font color=blue size=+1>The environmental variables:</font></b><br>\n";
print "<PRE>\n";

@keys = sort keys %ENV;
for $key (@keys) {
	print ("<font color=\"green\">$key</font> = $ENV{$key}\n");
}

print "</PRE>\n";
print "</BODY>\n";
print "</HTML>\n";


Perl