<-
Apache > HTTP Server > 文件 > 版本 2.4 > 操作說明 / 教學

Apache 教學:使用 CGI 建立動態內容

可用語言:  en  |  es  |  fr  |  ja  |  ko 

Support Apache!

另見

top

簡介

CGI(共通閘介面)定義了網頁伺服器與外部產生內容的程式互動的方式,此類程式通常稱為 CGI 程式或 CGI 腳本。這是使用最熟悉的程式語言在網站上放置動態內容的簡便方式。本文將簡介如何在 Apache 網路伺服器上設定 CGI,以及撰寫 CGI 程式的入門知識。

top

設定 Apache 以允許 CGI

為正常運行 CGI 程式,您需要設定 Apache 以允許執行 CGI。有數種做法。

注意:如果 Apache 已使用共享模組支援建置,您需要確保已載入模組;在 httpd.conf 中,您需要確認 LoadModule 指令未被註解。設定正確的指令可能類似這樣
LoadModule cgid_module modules/mod_cgid.so
在 Windows 或使用非執行緒 MPM(如 prefork)的情況下,設定正確的指令可能類似這樣
LoadModule cgi_module modules/mod_cgi.so

ScriptAlias

指令 ScriptAlias 告知 Apache 將特定目錄預留給 CGI 程式。Apache 會假設此目錄中的每個檔案都為 CGI 程式,並在客戶端請求該資源時嘗試執行。

指令 ScriptAlias 看起來像這樣

ScriptAlias "/cgi-bin/" "/usr/local/apache2/cgi-bin/"

如您將 Apache 安裝在預設位置,顯示的範例出自我電腦預設的 httpd.conf 組態檔案。ScriptAlias 指令與用於定義應對應到特定目錄的 URL 字首的 Alias 指令非常類似。AliasScriptAlias 通常會用於位於 DocumentRoot 目錄外部的目錄。AliasScriptAlias 之間的差異在於,ScriptAlias 有額外的意義,即在該 URL 字首底下的一切都會被視為 CGI 程式。因此,上述範例會告訴 Apache,任何以 /cgi-bin/ 開頭的資源請求都應使用 /usr/local/apache2/cgi-bin/ 目錄提供服務,並應做為 CGI 程式處理。

例如,如果要求的 URL 為 http://www.example.com/cgi-bin/test.pl,Apache 會嘗試執行檔案 /usr/local/apache2/cgi-bin/test.pl 並傳回輸出。當然,檔案必須存在且可執行,並且以特定方式傳回輸出,否則 Apache 會傳回錯誤訊息。

ScriptAlias 目錄外部的 CGI

考量安全性因素,CGI 程式通常會受限於 ScriptAlias'ed 目錄。藉此,管理員可以嚴格控管哪些人可以使用 CGI 程式。然而,如果採取適當的安全性防護措施,就沒有理由不能從任意目錄執行 CGI 程式。例如,您可能希望讓使用者在其內部目錄中擁有 Web 內容,且方法是使用 UserDir 指令。如果他們想擁有自己的 CGI 程式,但無法存取主 cgi-bin 目錄,就需要能夠在其他地方執行 CGI 程式。

要允許在任意目錄中執行 CGI,共有兩個步驟。首先,必須使用 AddHandlerSetHandler 指令啟用 cgi-script 處理常式。其次,必須在 Options 指令中指定 ExecCGI

明確使用 Options 允許執行 CGI

您可以在主伺服器組態檔案內部明確使用 Options 指令,以指定允許在特定目錄中執行 CGI。

<Directory "/usr/local/apache2/htdocs/somedir">
    Options +ExecCGI
</Directory>

上述指令會告訴 Apache 允許執行 CGI 檔案。您還需要告訴伺服器哪些檔案是 CGI 檔案。以下 AddHandler 指令會告訴伺服器將所有副檔名為 cgipl 的檔案都視為 CGI 程式。

AddHandler cgi-script .cgi .pl

.htaccess 檔案

如果您無法存取 `httpd.conf`,.htaccess 教學課程示範如何啟動 CGI 程式。

使用者目錄

若要允許執行使用者目錄中任何以 `cgi` 結尾的檔案的 CGI 程式,您可以使用以下設定。

<Directory "/home/*/public_html">
    Options +ExecCGI
    AddHandler cgi-script .cgi
</Directory>

如果您想指定使用者的目錄中的 `cgi-bin` 子目錄,並將其中所有內容當作 CGI 程式處理,可以使用以下設定。

<Directory "/home/*/public_html/cgi-bin">
    Options ExecCGI
    SetHandler cgi-script
</Directory>
top

撰寫 CGI 程式

``一般的'' 編寫程式和 CGI 編寫程式有兩個主要的相異處。

第一,您的 CGI 程式的所有輸出都必須先加上 MIME 類型 標頭。這是 HTTP 標頭,用來告訴客戶端所接收的是何種內容。大多數時候,會看起來像這樣

內容類型:文字/html

第二,您的輸出需要是 HTML 或瀏覽器可以顯示的其他格式。大多數時候,會是 HTML,但有時您可能會撰寫一個 CGI 程式來輸出 gif 影像,或其他非 HTML 內容。

除了這兩點以外,撰寫 CGI 程式的寫法會和其他您可能撰寫的程式非常類似。

您的第一個 CGI 程式

下列是一個示範的 CGI 程式,它會在您的瀏覽器列印一行。輸入下列內容,儲存成一個稱為 `first.pl` 的檔案,並放在您的 `cgi-bin` 目錄中。

#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello, World.";

即使您不熟悉 Perl,也應該看得出這裡發生了什麼事。第一行告訴 Apache(或您正在執行的 shell)這個程式可以用位於路徑 `usr/bin/perl` 的直譯器讀取檔案來執行。第二行列印我們談論過的內容類型宣告,接著是兩個換行符號。這會在標頭之後放置一空白行,以標示 HTTP 標頭的結束,以及本文的開始。第三行列印字串「Hello, World.」。就是這樣了。

如果您開啟您最愛的瀏覽器,然後告訴它讀取位址

http://www.example.com/cgi-bin/first.pl

或您放置檔案的任何位置,您會在瀏覽器視窗中看到單行文字 `Hello, World.`。這並不令人興奮,但一旦您讓它運作,您就能夠得到任何您想要運作的東西。

top

但它仍然不運作!

在您試圖從網路存取您的 CGI 程式時,您的瀏覽器中可能會出現四項基本項目。

您的 CGI 程式的輸出
太棒了!這表示一切正常。如果輸出正確,但瀏覽器沒有正確處理它,請確認您的 CGI 程式中有設定正確的 `內容類型`。
您的 CGI 程式的原始程式碼或「POST 方法不允許」訊息
表示您尚未適當設定 Apache 處理 CGI 程式。請重新閱讀設定 Apache區段,尋找您遺漏的部分。
「Forbidden」開頭的訊息
表示權限出問題。請檢查Apache 錯誤記錄,以及下方的檔案權限區段。
「Internal Server Error」訊息
如果您檢查了Apache 錯誤記錄,您可能會發現出現「Premature end of script headers」(指令碼標頭過早結束),以及您的 CGI 程式產生的錯誤訊息。在這種情況下,您需要檢查以下各區段,了解可能阻止您的 CGI 程式發出正確 HTTP 標頭的原因。

檔案權限

請記住,伺服器並非以您的身分執行。也就是說,當伺服器啟動時,它會使用不具特權的使用者權限(通常是 nobodywww)執行,因此會需要額外權限才能執行您擁有權限的檔案。通常,讓 nobody 擁有足夠權限來執行的檔案方式,就是對此檔案給予所有人都可執行的權限

chmod a+x first.pl

此外,如果您的程式會從其他檔案中讀取或寫入其他檔案,則這些檔案需要擁有正確的權限才能執行這項動作。

路徑資訊和環境

當您從指令列執行程式時,系統會傳遞特定資訊給 shell,而您無需手動執行。例如,您有一個 PATH,用來告知 shell 在何處找尋您參考的檔案。

當程式透過網路伺服器執行,作為 CGI 程式時,可能不會有相同的 PATH。您的 CGI 程式中會呼叫到的任何程式(例如 sendmail),都必須透過完整路徑指定,如此一來,shell 才能在嘗試執行您的 CGI 程式時找到它們。

此狀況的常見表現是,CGI 程式第一行中顯示的指令碼翻譯器路徑(通常是 perl),看起來類似

#!/usr/bin/perl

請確認這的確是翻譯器路徑。

在 Windows 上編輯 CGI 指令碼時,可能會在翻譯器路徑中附加換行字元。請確定以 ASCII 模式將檔案傳輸到伺服器。如果未執行此動作,由於作業系統會將無法辨識的換行字元視為翻譯器檔名的部分,因此可能會產生「指令未找到」警告。

缺少環境變數

如果您的 CGI 程式依賴非標準的環境變數,您需要確保 Apache 傳遞這些變數。

如果環境中缺少 HTTP 標頭,請務必遵照RFC 2616第 4.2 節的格式建立它們:標頭名稱必須以字母開頭,後續僅能接字母、數字或連字元。違反此規則的任何標頭都將靜默地刪除。

程式錯誤

大多數時候,當 CGI 程式失敗時,都是程式本身的問題。一旦你掌握這個 CGI 程式,且不再犯上文中提到的兩個錯誤時,這情況尤其明顯。首先要做的是確認在經由網路伺服器測試你的程式時,這個程式會在指令列執行。舉例來說,請嘗試

cd /usr/local/apache2/cgi-bin
./first.pl

(不要呼叫 Perl 這個直譯程式,shell 和 Apache 應該會在指令稿的第一列使用 路徑資訊 來找到這個直譯程式。)

你的程式寫的第一件事應當是一組 HTTP 標頭,其中包括 Content-Type,接著是一行空白列。如果你看到其他任何內容,如果你試圖透過伺服器執行它,Apache 會傳回 Script 標頭過早結束 錯誤。有關更多詳細資料,請參閱上述 寫入 CGI 程式

錯誤記錄

錯誤記錄是你的好夥伴。任何錯誤都會在錯誤記錄中產生訊息。你應該常常首先查看錯誤記錄。如果你架設網站的地方不允許你存取錯誤記錄,你或許應該將你的網站架設到別的地方。學習閱讀錯誤記錄,你會發現你幾乎所有的問題都能快速地識別出來,並快速地解決掉。

Suexec

支援程式 suexec 允許在不同的使用者權限下執行 CGI 程式,這取決於它們位於哪一個虛擬主機或使用者家目錄中。Suexec 執行非常嚴謹的權限檢查,而且任何檢查失敗都會導致你的 CGI 程式錯誤,並顯示 Script 標頭過早結束

如要查看你是否使用 suexec,請執行 apachectl -V,並檢查 SUEXEC_BIN 的位置。如果 Apache 在開機時找到 suexec 二進位檔,suexec 便會啟用。

除非你完全了解 suexec,否則你不應該使用它。如要停用 suexec,請移除 (或重新命名) SUEXEC_BIN 指向的 suexec 二進位檔,然後重新啟動伺服器。如果你在閱讀完有關 suexec 的內容後,仍然想要使用它,請執行 suexec -V,以找出 suexec 記錄檔的位置,並使用該記錄檔找出你違反哪些政策。

top

幕後發生的事?

當你成為更進階的 CGI 程式員時,瞭解幕後發生的事,將會對你有所幫助。特別是瀏覽器和伺服器如何彼此溝通。因為雖然撰寫一個印出「Hello, World」的程式是很棒,但它並未特別的有用處。

環境變數

當你使用電腦時,環境變數會作為值在你身邊飄動。它們有用的情況包括你的路徑(當你輸入時電腦會在其中尋找實作指令的實際檔案)、你的使用者名稱、你的終端機類型等等。若要查看你日常所有環境變數的完整清單,請在命令提示字元輸入 env

在 CGI 交易過程中,伺服器和瀏覽器也會設定環境變數,以利彼此通訊。舉例來說,它會包括瀏覽器類型(Netscape、IE、Lynx)、伺服器類型(Apache、IIS、WebSite)、正在執行的 CGI 程式的名稱等等。

這些變數可供 CGI 程式設計師使用,並形成 client-server 通訊的一半。必要變數的完整清單位於 Common Gateway Interface RFC

這個簡單的 Perl CGI 程式將顯示正在傳遞的所有環境變數。cgi-bin 目錄中包含兩個類似的程式。請注意,有些變數是必要的,而有些則是選用的,因此你可能會看到一些不在官方清單中的變數。此外,Apache 提供多種不同的方式供你為預設提供的基本變數 新增自己的環境變數

#!/usr/bin/perl
use strict;
use warnings;

print "Content-type: text/html\n\n";
foreach my $key (keys %ENV) {
    print "$key --> $ENV{$key}<br>";
}

STDIN 與 STDOUT

伺服器和 client 之間的其他通訊會透過標準輸入 (STDIN) 和標準輸出 (STDOUT) 進行。在一般日常情境下,STDIN 指的是鍵盤,或程式用來處置的檔案,而 STDOUT 通常指的是主控台或螢幕。

當你將網頁表單 POST 到 CGI 程式時,表單中的資料會以特殊格式打包,並透過 STDIN 傳送到你的 CGI 程式。然後,程式便能處理資料,就好像資料來自鍵盤或檔案一樣」

這種「特殊格式」很簡單。欄位名稱和其值會以等號 (=) 符號連接,而值對會以與號 (&) 連接。空格、與號和等號等不便的字元會轉換成其十六進位等值,以防阻礙作業。整個資料字串可能會如下所示

name=Rich%20Bowen&city=Lexington&state=KY&sidekick=Squirrel%20Monkey

有時你還會看到這種類型的字串附加到 URL。當這麼做時,伺服器會將該字串放入稱為 QUERY_STRING 的環境變數。這稱為 GET 要求。你的 HTML 表單會透過設定 FORM 標籤中的 METHOD 屬性來指定是用 GET 還是 POST 傳送資料。

然後,你的程式負責將該字串分割成有用的資訊。很幸運地,市面上有許多函式庫和模組可以協助你處理這些資料,並處理 CGI 程式的其他層面。

top

CGI 模組/函式庫

編寫 CGI 程式時,您應該考慮使用程式碼函式庫或模組,以協助您完成大多數繁瑣工作。這能減少錯誤,並加快開發速度。

如果您使用 Perl 撰寫 CGI 程式,則可在 CPAN 取得模組。最受歡迎的此類模組為 CGI.pm。您也可以考慮 CGI::Lite,它實作一組最基本的功能,這已足以應付大部分程式。

如果您使用 C 編寫 CGI 程式,有多種選擇。其中之一是來自 https://web.mit.edu/wwwdev/www/cgic.htmlCGIC 函式庫。

top

如需更多資訊

您可以於 Common Gateway Interface RFC 取得目前的 CGI 規範。

當您在寄件清單或新聞群組張貼關於 CGI 問題的疑問時,請務必提供有關發生狀況、預期發生狀況、實際發生狀況有何不同、您執行的伺服器、CGI 程式使用的語言以及(如果可能)造成問題的程式碼等充足資訊。這將使找出您的問題容易得多。

請注意,除非您確定已在 Apache 原始碼中找到問題,否則絕不要將有關 CGI 問題的疑問張貼至 Apache 錯誤資料庫。

可用語言:  en  |  es  |  fr  |  ja  |  ko 

top

意見

請注意
此處並非問答區。張貼於此的留言應針對改進文件或伺服器的建議,而且如果已實作或被視為無效/離題,則可能會被我們的管理員移除。關於如何管理 Apache HTTP 伺服器的疑問應導向我們的 IRC 頻道 (#httpd),請至 Libera.chat 或寄送至我們的 寄件清單