[教學] CGI 程式設計
一、CGI 程式設計
什麼是 CGI ? 什麼是 CGI 程式?
CGI 是 Common Gateway Interface 的簡稱,一般中譯為:共通閘道介面。
Common 是指共通的、不限於一種作法的意思,在這裡指的是語言的獨立性,換言之,可以用任何一種程式語言來實作。
Gateway 是說伺服器的應用能力是經由擴充得到的,它將查詢等要求交由外部程式處理,處理完了之後,再將結果傳回。
Interface 是指一種介面的意思,符合此一介面標準,便可以輕鬆地製作外部程式,而無需去擔心或處理低階的問題。
由於網際網路上的 WEB 伺服器 (我們稱為 Server 端) 採用 HTTP 協定,該協定只是單純的回應客戶端 (我們稱為 Client 端,通常是使用者的瀏覽器) 的文件要求,即 Client 端要求什麼,Server 就傳回什麼,因此在 HTTP 協定下,Server 只有傳回靜態文件的能力而已,無法像各種平台上的伺服器一樣,藉由執行各類應用程式,來達成客戶端的各種查詢或交易等要求。
為了彌補此一缺點,WEB 伺服器勢必要藉助外部程式來執行 Client 端的要求,再將處理結果傳回給 Client 端。例如:使用者輸入一個關鍵字,要求列出某些書籍的作者或書名,Server 接收到這個關鍵字之後,交由外部程式去處理,通常是由資料庫中去尋找,最後將尋著的結果回應給 Server,Server 再將它傳回給瀏覽器端的使用者。
CGI 就是提供給外部程式的一種介面,只要符合此一介面標準,程式設計師便可以輕鬆地使用該平台上支援的任何一種程式語言來撰寫外部程式,於是我們說:凡是符合 CGI 介面標準的外部程式,就稱之為 CGI 程式。
撰寫 CGI 程式,常見的語言有 Perl、Python、C、C++、VB,甚至某些 UNIX 中的 shell script 都有人使用。
我們可以這樣來看待 CGI 以及 CGI 程式:
CGI 程式是 WEB 伺服器應用能力的擴充(Extension),而 CGI 則是此一擴充能力的介面,熟悉瞭解此一介面的原理及運作方式,我們便可以為 WEB 伺服器建立即時互動的能力,以滿足客戶端的要求。
-------------------------------------------------------------------------------
二、撰寫 CGI 程式需要那些工具?
一般而言,撰寫 CGI 程式,所需要的工具並不多,也不需要花大錢,各項工具都可以輕鬆找到。
* 純文字的編輯器,例如:
o Win95/98 中的記事本
o PE II
o 漢書
o UltraEditor(推薦)
o vi (Unix 系統中的編輯器,推薦)
o Emacs (Unix 系統中的編輯器,推薦)
o 其它
* 選用一套語言編譯器,例如:
o 若打算用 Perl 來撰寫 CGI 程式,則需 Perl 編譯器
通常,若想在自己的機器上測試 CGI 程式,才需要
自行安裝一套。
o C/C++ 編譯器
o 其它語言編譯器,視你所選用的語言而定
-------------------------------------------------------------------------------
三、CGI 程式設計如何入門?
1.您必須有點程式語言的基礎,不需要太高深,但至少基本的觀念要了解,若無,那麼您就應該先行補足這一方面的知識,否則 CGI 程式設計對您而言,可能會是一種很痛苦的挫折。
2.了解 CGI 程式的基本觀念和原理,最好找一本 CGI 的入門書籍來看看。
3.觀摩別人的程式,由模仿開始。CGI資源索引 這個站台是您取得CGI觀摩程式的好地方!
4.動手實作 CGI 小程式,由實作中磨鍊紮實的實務經驗,並將各種情況記錄下來,不管成功或失敗。
5.不斷地培養 CGI 偵錯的能力和經驗,CGI 程式設計比其它程式設計需要更多一點的耐心和細心,因為 CGI 程式的偵錯,並不是很容易,只要一點小小的不注意,便可能產生錯誤的結果,而且可能找了老半天也找不出錯誤之處何在?!因此,剛入門時,錯誤的情況是彌足珍貴的磨鍊機會,千萬不要灰心喪志,輕言放棄,最好將它記錄下來,然後先將問題單純化,分割問題成小單位,再逐步檢查。這部份的經驗是CGI程式設計的入門功夫,切切不可小看它喔!
6.多和別人討論並交換心得經驗,多看相關的 News 信區。
7.自定一個中型程式為目標,轉變模仿的作法,加入自己的創意和想法,開始建立自己的風格。
8.不斷地看書、吸收新知,尋求突破和進階。
-------------------------------------------------------------------------------
四、第一支 CGI 程式
#! /usr/bin/perl
# (第一列用來指出 perl 的路徑)
# 這是註解
# (第二列開始,若有出現 # ,則表示該列為註解,
# perl 將不會執行它)
print "Content-type:text/html\n\n";
# 這是 MIME 表頭,用來告訴瀏覽器要送出的資料型態是什麼?
# 一定要寫出這一行,否則會出現 Error 500 錯誤
# 注意:每列命令用 ; 做為結束!
# 接下來印出欲顯示的網頁內容
print "<html><head><title>CGI 哈囉!</title></head>";
print "<body bgcolor='white'>";
print "<h1>Hello, world!!</h1>";
print "</body></html>";
-------------------------------------------------------------------------------
五、執行方法
1. 將 CGI 程式用 FTP 軟體送上網頁 cgi-bin 目錄中
2. 附屬檔名通常為 .cgi 或 .pl
3. 注意一定要用 ASCII 模式上傳,否則會出現 Error 500 錯誤
4. 將 CGI 程式屬性改為 755
5. 在您的瀏覽上位址列處鍵入:http://主機位址/cgi-bin/hello.cgi
-------------------------------------------------------------------------------
六、如何取得傳遞資料?
一般而言,欲將處理資料傳遞給伺服器,大多使用以下二種方法:
1.GET:
此法會將資料以鍵和值(Key-Value)成對的方式,附加在 URL 之中,若有兩項以上的資料,則各對鍵值之間用 & 隔開。
例如:
http://主機位址/cgi-bin/test.cgi?name=John&age=18
即是將 (name, John) 以及 (age, 18) 二組鍵值以 GET 的方式傳遞。當然也可以利用 HTML 語法中的 Form 表單,將 method (傳遞方法)設為 GET。
所謂鍵值,其實就是變數的概念,鍵(Key)就是變數的名稱,值(Value)就是變數的內含。如果您要將一個內含為 John 的變數 name,傳遞給伺服器,那麼只要在 URL 之後加上個問號(?),並以 name=John 的方式附加上去即可。
GET 的缺點是:URL 字串長度有一定的限制,若鍵值太多太長,可能會使得部份資料被截掉,而無法傳送至伺服器。
以 GET 的方式傳遞時,伺服器會將資料放入環境變數 QUERY_STRING 之中,我們必須由這個環境變數來取得資料。
2.POST:
這種方式和 GET 作法迴異,POST 利用 HTML 語法的 Form 表單,由瀏覽器將鍵值傳給伺服器,並放入伺服器的標準輸出入機制中,由於它沒有 GET 的缺點,因此可以傳送較多的資料,一般而言,我們大多使用 POST 的方式。欲使用 POST 方式,要在表單中將方法 (method) 設為 POST 才行。以 POST 的方式傳遞時,我們必須由標準輸出入來取得資料,資料的長度,伺服器將它記錄在環境變數 CONTENT_LENGTH 中。
範本:
若以 GET 的方式傳遞,例如 http://www.ols3-cgi.com/cgi-bin/test.cgi?name=John&age=18
則以下列方式來取得資料:
$temp = $ENV{'QUERY_STRING'};
此時 $temp 中的內含即為 name=John&age=18
若以 POST 的方式傳遞,則以下列方式來取得資料:
read(STDIN, $temp, $ENV{'CONTENT_LENGTH'});
此時 $temp 中的內含即為 name=John&age=18
若您使用 CGI.pm 模組的話,那麼取得資料的方法更是十分簡單易懂,方法如下:
use CGI; # 宣告使用 CGI.pm 模組
$q=CGI->new(); # 要求配置一個 CGI 物件
$user_name=$q->param('name'); # 取得變數 name 的內含
$user_age=$q->param('age'); # 取得變數 age 的內含
此時,$user_name 的內含即為 John,$user_age 的內含則為 18
-------------------------------------------------------------------------------
七、如何將傳遞資料解碼?
某些字元對伺服器而言具有特殊意義,若欲傳遞給 CGI 程式處理的資料中含有這些字元,那麼瀏覽器會對這些字元予以編碼,CGI 程式在取得這些經過編碼的資料後,必須進行解碼的動作,才能將傳遞的資料予以還原。
範本:
解碼的方法:
if ($ENV{'REQUEST_METHOD'} eq 'POST') {
read(STDIN, $temp, $ENV{'CONTENT_LENGTH'});
} else {
$temp=$ENV{'QUERY_STRING'};
}
@key_value=split(/&/,$temp);
foreach $item(@key_value) {
($key,$value)=split (/=/,$item,2);
$value=~tr/+/ /;
$value=~ s/%(..)/pack("c",hex($1))/ge;
$data{$key}=$value;
}
此時 %data 這個雜湊陣列即取得了解碼後的資料。
-------------------------------------------------------------------------------
八、如何製作簡易訪客留言板?
(一)製作訪客留言表單 signin.htm
您可以使用網頁編輯器來快速製作,也可以手動用 HTML 語法來撰寫。
以下是 signin.htm 的內容:
<html>
<head>
<!-- 加上以下這一行,以免有亂碼產生 -->
<meta http-equiv="Content-Type" c>
<title>留言板</title>
</head>
<body bgcolor="white">
<hr size=1>
<center>
<!--以下是您的站台名稱-->
<font size=5 color=red>我的訪客留言板</font>
<br>
<!--以下是您的版權宣告-->
<font color="green" size="2"> v1.0 精簡版 (C) 1997 written by OLS3</font>
<hr size=1>
<table align=center border=0><tr><td>
<!--以下是表單內容, 共有二個欄位: 姓名及留言內容, 您可以自行再增添;
表單採 POST 呼叫法, cgi 程式名稱是 GBK.cgi -->
<form name="ols3" method="post" action="http://主機/cgi-bin/GBK.cgi">
<!--姓名欄位的名稱是 name-->
姓名: <input type="text" name="name" size="30">
<br>
<!--留言內容欄位的名稱是 comment-->
留言內容:<br>
<textarea name="comment" rows="5" cols="45"></textarea>
<p>
<input type="submit" value="確定"> <input type="reset" value="清除">
<input type="button" value="回上一頁" >
</form>
</td></tr></table></center>
</body>
</html>
(二)製作留言寫入程式 GBK.cgi
留言寫入的方式有許多種,這裡介紹的是靜態標記法。
所謂靜態標記法是說:將留言內容,直接寫入某一個 HTML 檔中,該 HTML 檔,
事先即存在一個特殊的標記,此標記的目的,是用來識別留言要從何處開始寫入,
也就是說,在該標記之後,就是留言開始要寫入的地方。
將來若要顯示留言內容時,只要直接超連結至這個 HTML 檔即可!
這種方式的留言板算是最簡單輕便的一種。
寫入程式 GBK.cgi 如下:
#!/usr/local/bin/perl
# 先定義留言寫入的 HTML 檔案位置及名稱
$gbkhtml="/var/www/html/gbk.html";
# 設定留言板的 URL 位址
$gbkurl="http://主機/gbk.html";
# 取得表單傳遞過來的內容
read(STDIN, $temp, $ENV{'CONTENT_LENGTH'});
@pairs=split(/&/,$temp);
foreach $item(@pairs) {
($key,$content)=split (/=/,$item,2);
$content=~tr/+/ /;
$content=~ s/%(..)/pack("c",hex($1))/ge;
$OLS3{$key}=$content;
}
#經過上述動作,此時留言者姓名及內容,已分別放在 $OLS3{'name'} 及 $OLS3{'comment'} 中
$name=$OLS3{'name'};
$comment=$OLS3{'comment'};
# 處理斷行問題
$comment=~ s/\cM\n/<br>\n/g;
# 取得留言時間
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=gmtime(time+8*60*60);
if (length ($min) == 1) {$min = '0'.$min;}
if (length ($sec) == 1) {$sec = '0'.$sec;}
$mon+=1;
$date="$mon/$mday/$year, $hour:$min:$sec";
# 開啟HTML檔, 並讀出所有的內容
open(FHD, "$gbkhtml") || die "Content-type: text/html\n\n 開啟檔案錯誤!\n";
@all=<FHD>;
close(FHD);
# 重新開啟HTML檔, 以便更新其留言內容
open(FHD, ">$gbkhtml") || die "Content-type: text/html\n\n 開啟檔案錯誤!\n";
# 利用迴圈, 將原檔案內容依次寫回, 並在其過程中寫入新的留言
foreach $line(@all) {
# 該列是否為標記? 若是, 表示該處為新留言開始要寫入的地方
if ($line =~ /<!--ols3-->/) {
# 寫回原來的標記, 以後方可繼續使用
print FHD "<!--ols3-->\n";
# 寫入新的留言
print FHD "姓名: $name [時間: $date]<p>$comment<hr size=1>\n";
} else {
# 若該列不是標記, 表示是舊有的內容, 則不予改變地將它寫回
print FHD $line;
}
}
# 關畢 HTML 檔
close(FHD);
# 接下來, 感謝一下留言的網友, 三秒後回到顯示留言畫面
print "Content-type: text/html\n\n";
print "<html><head>\n";
print "<META HTTP-EQUIV=REFRESH CONTENT=\"3;URL=$gbkurl\">\n";
print "<title>Thanks!</title></head>\n";
print "<body bgcolor=white><center>\n";
print "謝謝您的留言!\n";
print "</center></body></html>\n";
# 程式結束
將程式放入 cgi-bin 中
GBK.cgi 屬性設為 755,放入 cgi-bin 目錄中
gbk.html 屬性設為 666 ,放入您的網頁空間中
gbk.html 一開始尚未有任何留言寫入時的樣子如下:
<html>
<head>
<meta http-equiv="Content-Type" c>
<title>我的留言板</title>
</head>
<body bgcolor=white>
<center>
<font size=5 color=red>我的留言板</font>
<hr size=1>
<a href="http://主機/signin.htm">我要留言</a>
<hr size=1>
</center>
<!--ols3-->
</body>
</html>
(三) 顯示訪客留言:gbk.html
接下來,您就可以設一個超連結來顯示您的訪客留言囉。
例如:
<A href="http://主機/gbk.html">訪客留言</a>
-------------------------------------------------------------------------------
九、CGI 程式常見疑難
為何我的 CGI 程式執行時,出現 Server Error 500 的錯誤訊息?
見到的畫面如下所示:
The server encountered an internal error or misconfiguration and was
unable to complete your request. Please contact the server administrator,
mmm@nnn.com and inform them of the time the error occurred, and anything
you might have done that may have caused the error.
More information about this error may be available in the server error log.
CGI 程式出現 Server Error 500 的現象很平常,不管是 CGI 程式的新手或老手,都經常會碰到這個情況。 So,不要灰心! :-)
以下是我寫作 CGI 程式幾年來的一些偵錯經驗,提供給各位做參考:
1.CGI 程式中沒有印出: 欲輸出內容的 MIME 型態.
一般而言, 就是沒傳回 Content-type: text/html 再加上一列空白行分隔.
請檢查您的 CGI 程式是否有 print "Content-type: text/html\n\n";
2. 您的 CGI 程式呼叫 Perl 的 magic code 路徑不對.
在程式的第一行要放上 Perl 的呼叫路徑.
如 #! /usr/bin/perl 或 #! /usr/local/bin/perl
您可以 telnet 進主機之後, 下 which perl 指令來尋找.
或者向網管人員洽詢, 務必要查清楚 Perl 的正確路徑.
3. 欲開啟或寫入的檔案, 其絕對路徑或相對路徑錯誤.
例如 $guestbook_file="/home/your_dir/www/gbk.txt";
請查清楚此檔案的路徑是否正確?
4. 欲開啟或寫入的檔案或目錄, 其權限沒設好.
通常要寫入的檔案, 權限須設為 666, 僅供讀取的檔案, 權限設為 755.
或者讓 nobody 身份擁有寫入或讀取權.
5. 上傳 CGI 程式時, 沒有用 ASCII 模式上傳.
您是否錯用 Binary 模式上傳呢? 再用 ASCII 模式上傳一次吧?!
6. CGI 程式的語法有誤, 可能是忘了在某一列 Perl 敘述之後, 加上 ';' 這個結束符號,
或是忘了加上括號() { }. 或者是其它語法錯誤所致.
7. CGI 程式本身的權限屬性沒設好, 通常 CGI 程式設為 755, 或者讓 nobody 身份擁有讀取及執行權.
8. 您可能是在印出 html 語法時, 忘了把雙引號中的其它雙引號給 meta 掉(或稱作 escape 掉).
例如: print "<font color="red">Hello</font>\n"; 便是錯誤的語法.
應改成 print "<font color=\"red\">Hello</font>\n";
也就是說在雙引號中, 若要有雙引號, 應寫成 \" 來 替代 " .
比較方便的做法是使用 perl 的 qq, 可將上面改寫成:
print qq(<font color="red">Hello</font>\n);
或者, 使用 Here document 的寫法:
print <<HERE;
<font color="red">Hello</font>
..............................
HERE
9. 上傳至 Unix/Linux/FreeBSD 主機之後, 每列結尾出現 ^M 的符號.
請將這些符號刪除即可.
10.若您的程式有利用 DBI+DBD 撰寫 SQL 資料庫程式,
則當某些 SQL 語法錯誤時, 也會產生 Error 500.
11.您的程式有使用 die 語法來 catch error, 而沒有做適當的 "錯誤或例外處理".
12.您 require 某一個 perl file, 但該 perl file 並不存在, 或者路徑錯誤.
13.您引用的 Perl 模組(module), 並未安裝好,
或者, 您忘了加 use 語法來引用模組, 卻用了某一模組的函式功能.
14.在 Win95/98 中試圖使用 flock 語法來 "鎖定/解開" 檔案, 也會發生 Error 500.
-------------------------------------------
|