# CGI動態頁面
## 簡介
相關模塊
* `mod_alias`
* `mod_cgi`
相關指令
* `AddHandler`
* `Options`
* `ScriptAlias`
CGI(公共網關接口)定義了web服務器與外部內容生成程序之間交互的方法,通常是指CGI程序或者CGI腳本,它是在網站上實現動態頁面的最簡單和常用的方法。本文將對如何在Apache web服務器上建立CGI以及如何編寫CGI程序進行介紹。
## 配置Apache以允許CGI
要讓CGI程序能正常運作,必須配置Apache以允許CGI的執行,其方法有多種。
### ScriptAlias
`ScriptAlias`指令使Apache允許執行一個特定目錄中的CGI程序。當客戶端請求此特定目錄中的資源時,Apache假定其中所有的文件都是CGI程序并試圖運行它。
`ScriptAlias`指令形如:
```
ScriptAlias /cgi-bin/ /usr/local/apache2/cgi-bin/
```
如果Apache被安裝到默認位置,默認的配置文件`httpd.conf`中就會有上述配置。`ScriptAlias`與`Alias`指令非常相似,都是定義了映射到一個特定目錄的URL前綴,兩者一般都用于指定位于`DocumentRoot`以外的目錄,其不同之處是`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`指定的目錄中,這樣,管理員就可以嚴格控制誰可以使用CGI程序。但是,如果采取了恰當的安全措施,則沒有理由不允許其他目錄中的CGI程序運行。比如,你可能希望用戶在`UserDir`指定的宿主目錄中存放頁面,而他們有自己的CGI程序,但無權訪問`cgi-bin`目錄,這樣,就產生了運行其他目錄中CGI程序的需求。
允許CGI在任意目錄執行需要兩個步驟:第一步,必須用`AddHandler`或`SetHandler`指令激活`cgi-script`處理器。第二步,必須在`Options`指令中啟用`ExecCGI`選項。
### 用Options顯式地允許CGI的執行
可以在主配置文件中,使用`Options`指令顯式地允許特定目錄中CGI的執行:
```
<Directory /usr/local/apache2/htdocs/somedir>
Options +ExecCGI
</Directory>
```
上述指令使Apache允許CGI文件的執行。另外,還必須告訴服務器哪些文件是CGI文件。下面的`AddHandler`指令告訴服務器所有帶有`cgi`或`pl`后綴的文件是CGI程序:
```
AddHandler cgi-script .cgi .pl
```
### .htaccess文件
[`.htaccess`指南](#calibre_link-222)示范了怎樣在沒有權限修改`httpd.conf`文件的情況下激活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>
```
## 編寫CGI程序
編寫CGI程序和"常規"程序之間有兩個主要的不同。
首先,在CGI程序的所有輸出前面必須有一個HTTP的[MIME類型](#calibre_link-223 "see glossary")的頭,對客戶端指明所接收內容的類型,大多數情況下,像這樣:
```
Content-type: text/html
```
其次,輸出要求是HTML形式的,或者是瀏覽器可以顯示的其他某種形式。多數情況下,輸出是HTML形式的,但偶然也會輸出一個gif圖片或者其他非HTML的內容。
除了這兩點,編寫CGI程序和編寫其他程序大致相同。
### 第一個CGI程序
這個CGI程序的例子在瀏覽器中打印一行文字。把下列存為`first.pl`文件,并放在你的`cgi-bin`目錄中。
```
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello, World.";
```
即使不熟悉Perl語言,你也應該能看出它干了什么。第一行,告訴Apache這個文件可以用`/usr/bin/perl`(或者任何你正在使用的shell)解釋并執行。第二行,打印上述要求的內容類型說明,并帶有兩個換行,在頭后面留出空行,以示HTTP頭的結束。第三行,打印文字"Hello, World."。程序到此結束。
打開你喜歡的瀏覽器并輸入地址:
```
http://www.example.com/cgi-bin/first.pl
```
或者是你存放程序的其他位置,就可以在瀏覽器窗口中看到一行:`Hello, World.` 。雖然并不怎么激動人心,但是一旦這個程序能正常運行,那么就可能運行其他任何程序。
## 程序還是不能運行!
使用瀏覽器從網絡訪問CGI程序,可能會發生四種情況:
CGI程序的輸出
太好了!這說明一切正常。如果輸出正常但是瀏覽器處理出錯,請確認你在CGI程序中使用了正確的 `Content-Type` 。
CGI程序的源代碼或者一個"POST Method Not Allowed"消息
這說明Apache沒有被正確配置以執行CGI程序,重新閱讀[配置Apache](#calibre_link-224)看看遺漏了什么。
一個以"Forbidden"開頭的消息
這說明有權限問題。參考[Apache錯誤日志](#calibre_link-225)和下面的[文件權限](#calibre_link-226)。
一個"Internal Server Error"消息
查閱[Apache錯誤日志](#calibre_link-225),可以找到CGI程序產生的出錯消息"Premature end of script headers"。對此,需要檢查下列各項,以找出不能產生正確HTTP頭的原因。
### 文件的權限
記住,服務器不是以你的用戶身份運行的,在服務器啟動后,擁有的是一個非特權用戶的權限(通常是`nobody`或`www`)而需要更大的權限以允許文件的執行。通常,給予`nobody`足夠的權限以執行文件的方法是,對文件賦予任何人皆可執行的權限:
```
chmod a+x first.pl
```
另外,如果需要對其他文件進行讀取或寫入,也必須對這些文件賦予正確的權限。
### 路徑信息和環境變量
當你在命令行執行一個程序,某些信息會自動傳給shell而無須你操心,比如`PATH` ,告訴shell你所引用的文件可以在哪兒找到。
但是,在CGI程序通過web服務器執行時,則沒有此`PATH` ,所以,你在CGI程序中引用的任何程序(如`sendmail`)都必須指定其完整的路徑,使shell能找到它們以執行你的CGI程序。
一種普通的用法是,在CGI程序的第一行中指明解釋器(通常是`perl`),形如:
```
#!/usr/bin/perl
```
必須保證它的確指向解釋器。
另外,如果CGI程序依賴于某些[環境變量](#calibre_link-227),你要確保所需要的變量已經正確的由Apache進行了傳遞。
### 程序錯誤
多數CGI程序失敗的原因在于程序本身有問題,尤其是在已經消除上述兩種錯誤而CGI掛起的情況下。在用瀏覽器測試以前,先在命令行中執行你的程序,能夠發現大多數的問題。比如:
```
cd /usr/local/apache2/cgi-bin
./first.pl
```
(不要調用`perl`解釋程序,因為shell和Apache會根據腳本第一行的[路徑信息](#calibre_link-228)找到解釋器)
你最先看到的輸出內容應當是一組HTTP頭,包括`Content-Type`和結尾的空行。如果你看到了別的什么東西,那么當你在服務器上試運行時,Apache會返回 `Premature end of script headers` 錯誤。參見上面的[編寫CGI程序](#calibre_link-229)以獲得更多信息。
### 錯誤日志
錯誤日志是你的朋友。任何錯誤都會在錯誤日志中有所記載,所以你應該首先查看它。如果你的網站空間提供者不允許訪問錯誤日志,那么你應該考慮換一個空間提供者。學會閱讀錯誤日志,可以快速找出問題并快速解決。
### Suexec
[suexec](#calibre_link-230)允許CGI程序根據其所在虛擬主機或用戶宿主目錄的不同而以不同的用戶權限運行。suexec有極其嚴格的權限校驗,任何校驗失敗都會使CGI程序遭遇 `Premature end of script headers` 錯誤。
為了檢查你是否使用了suexec ,運行 `apachectl -V` 并檢查`SUEXEC_BIN`的位置。如果Apache在啟動時發現`suexec`二進制文件正存在于此,那么suexec將會被激活。
除非你很精通suexec,否則請不要使用它。要禁用它,只要刪除(或重命名)`SUEXEC_BIN`所指定位置的`suexec`二進制文件并重啟服務器就可以了。如果你又想啟用它,請首先閱讀[suexec文檔](#calibre_link-230)以詳細了解其運行機制,然后運行 `suexec -V` 命令找到suexec日志文件,并使用該日志文件找到你違反了哪條判斷規則。
## 幕后是怎樣操作的?
當你的CGI編程逐漸深入,理解幕后的操作(尤其是瀏覽器和服務器之間是如何通訊的)就變得很有用了。因為雖然成功地寫了一個程序打印"Hello, World",但并沒有實際的用處。
### 環境變量
環境變量是使用計算機時到處都會用到的變量,比如路徑(對實際文件的一個搜索路徑以補全你的輸入)、你的用戶名以及你的終端類型等等。在命令行輸入 `env` ,可以得到當天標準的環境變量列表。
在CGI處理過程中,服務器和瀏覽器都會設置環境變量,比如瀏覽器類型(Netscape、IE、Lynx)、服務器類型(Apache、IIS、WebSite)以及將要執行的CGI程序名稱等等。
所有這些變量對CGI程序員都有效,但只是客戶端-服務器通訊的一半內容。完整的變量列表參見[http://hoohoo.ncsa.uiuc.edu/cgi/env.html](http://hoohoo.ncsa.uiuc.edu/cgi/env.html)
這個簡單的CGI程序列出了所有的環境變量,Apache發行版的`cgi-bin`目錄中還有一個類似的程序。注意,有些變量是必須的,有些則是可選的,所以你可能會看見一些官方列表中沒有的變量。另外,Apache有多種方法可以在默認提供的變量之外[增加你的專用環境變量](#calibre_link-232)。
```
#!/usr/bin/perl
print "Content-type: text/html\n\n";
foreach $key (keys %ENV) {
print "$key --> $ENV{$key}<br>";
}
```
### STDIN 和 STDOUT
服務器和客戶端之間的其他通訊都通過標準輸入設備(`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程序達成其他目的的處理器。
## CGI模塊/庫
編寫CGI程序時,你應該考慮使用代碼庫或模塊來完成大多數瑣碎的工作,以減少錯誤并更快地開發。
如果用Perl語言編寫CGI程序,可用的模塊見[CPAN](http://www.cpan.org/) ,最常用的模塊是`CGI.pm` 。也可以考慮用`CGI::Lite` ,它實現了一個在多數程序中所有必須的最小功能集。
如果用C語言編寫CGI程序,則有很多選擇,其中之一是`CGIC`庫,來自[http://www.boutell.com/cgic/](http://www.boutell.com/cgic/)
## 更多信息
網上有大量的CGI資源。可以在Usenet組[comp.infosystems.www.authoring.cgi](news:comp.infosystems.www.authoring.cgi)和別人討論CGI相關問題。HTML Writers Guild 的郵件列表是一個優秀的問題解答資源。更多資源在[http://www.hwg.org/lists/hwg-servers/](http://www.hwg.org/lists/hwg-servers/)
另外,還可以閱讀CGI規范,其中有CGI程序操作的所有細節,原始版本見[NCSA](http://hoohoo.ncsa.uiuc.edu/cgi/interface.html) ,另有一個更新草案見[Common Gateway Interface RFC project](http://web.golux.com/coar/cgi/)
當你向一個郵件列表或者新聞組提交CGI相關問題時,你應該確保提供了足夠的信息以更容易地發現并解決問題,諸如:發生了什么事、你希望得到什么結果、結果與你所期望的有什么出入、你運行的服務器、CGI程序是用什么語言編寫的、如果可能就提供那個討厭的代碼。
注意,**不要**把CGI相關問題提交到Apache bug數據庫,除非你堅信發現的是Apache源代碼中的問題。
- Apache HTTP Server Version 2.2 文檔 [最后更新:2006年3月21日]
- 版本說明
- 從1.3升級到2.0
- 從2.0升級到2.2
- Apache 2.2 新特性概述
- Apache 2.0 新特性概述
- The Apache License, Version 2.0
- 參考手冊
- 編譯與安裝
- 啟動Apache
- 停止和重啟
- 配置文件
- 配置段(容器)
- 緩沖指南
- 服務器全局配置
- 日志文件
- 從URL到文件系統的映射
- 安全方面的提示
- 動態共享對象(DSO)支持
- 內容協商
- 自定義錯誤響應
- 地址和端口的綁定(Binding)
- 多路處理模塊
- Apache的環境變量
- Apache處理器的使用
- 過濾器(Filter)
- suEXEC支持
- 性能方面的提示
- URL重寫指南
- Apache虛擬主機文檔
- 基于主機名的虛擬主機
- 基于IP地址的虛擬主機
- 大批量虛擬主機的動態配置
- 虛擬主機示例
- 深入研究虛擬主機的匹配
- 文件描述符限制
- 關于DNS和Apache
- 常見問題
- 經常問到的問題
- Apache的SSL/TLS加密
- SSL/TLS高強度加密:緒論
- SSL/TLS高強度加密:兼容性
- SSL/TLS高強度加密:如何...?
- SSL/TLS Strong Encryption: FAQ
- 如何.../指南
- 認證、授權、訪問控制
- CGI動態頁面
- 服務器端包含入門
- .htaccess文件
- 用戶網站目錄
- 針對特定平臺的說明
- 在Microsoft Windows中使用Apache
- 在Microsoft Windows上編譯Apache
- Using Apache With Novell NetWare
- Running a High-Performance Web Server on HPUX
- The Apache EBCDIC Port
- 服務器和支持程序
- httpd - Apache超文本傳輸協議服務器
- ab - Apache HTTP服務器性能測試工具
- apachectl - Apache HTTP服務器控制接口
- apxs - Apache 擴展工具
- configure - 配置源代碼樹
- dbmmanage - 管理DBM格式的用戶認證文件
- htcacheclean - 清理磁盤緩沖區
- htdbm - 操作DBM密碼數據庫
- htdigest - 管理用于摘要認證的用戶文件
- httxt2dbm - 生成RewriteMap指令使用的dbm文件
- htpasswd - 管理用于基本認證的用戶文件
- logresolve - 解析Apache日志中的IP地址為主機名
- rotatelogs - 滾動Apache日志的管道日志程序
- suexec - 在執行外部程序之前切換用戶
- 其他程序
- 雜項文檔
- 與Apache相關的標準
- Apache模塊
- 描述模塊的術語
- 描述指令的術語
- Apache核心(Core)特性
- Apache MPM 公共指令
- Apache MPM beos
- Apache MPM event
- Apache MPM netware
- Apache MPM os2
- Apache MPM prefork
- Apache MPM winnt
- Apache MPM worker
- Apache模塊 mod_actions
- Apache模塊 mod_alias
- Apache模塊 mod_asis
- Apache模塊 mod_auth_basic
- Apache模塊 mod_auth_digest
- Apache模塊 mod_authn_alias
- Apache模塊 mod_authn_anon
- Apache模塊 mod_authn_dbd
- Apache模塊 mod_authn_dbm
- Apache模塊 mod_authn_default
- Apache模塊 mod_authn_file
- Apache模塊 mod_authnz_ldap
- Apache模塊 mod_authz_dbm
- Apache模塊 mod_authz_default
- Apache模塊 mod_authz_groupfile
- Apache模塊 mod_authz_host
- Apache模塊 mod_authz_owner
- Apache模塊 mod_authz_user
- Apache模塊 mod_autoindex
- Apache模塊 mod_cache
- Apache模塊 mod_cern_meta
- Apache模塊 mod_cgi
- Apache模塊 mod_cgid
- Apache模塊 mod_charset_lite
- Apache模塊 mod_dav
- Apache模塊 mod_dav_fs
- Apache模塊 mod_dav_lock
- Apache模塊 mod_dbd
- Apache模塊 mod_deflate
- Apache模塊 mod_dir
- Apache模塊 mod_disk_cache
- Apache模塊 mod_dumpio
- Apache模塊 mod_echo
- Apache模塊 mod_env
- Apache模塊 mod_example
- Apache模塊 mod_expires
- Apache模塊 mod_ext_filter
- Apache模塊 mod_file_cache
- Apache模塊 mod_filter
- Apache模塊 mod_headers
- Apache模塊 mod_ident
- Apache模塊 mod_imagemap
- Apache模塊 mod_include
- Apache模塊 mod_info
- Apache模塊 mod_isapi
- Apache模塊 mod_ldap
- Apache模塊 mod_log_config
- Apache模塊 mod_log_forensic
- Apache模塊 mod_logio
- Apache模塊 mod_mem_cache
- Apache模塊 mod_mime
- Apache模塊 mod_mime_magic
- Apache模塊 mod_negotiation
- Apache模塊 mod_nw_ssl
- Apache模塊 mod_proxy
- Apache模塊 mod_proxy_ajp
- Apache模塊 mod_proxy_balancer
- Apache模塊 mod_proxy_connect
- Apache模塊 mod_proxy_ftp
- Apache模塊 mod_proxy_http
- Apache模塊 mod_rewrite
- Apache模塊 mod_setenvif
- Apache模塊 mod_so
- Apache模塊 mod_speling
- Apache模塊 mod_ssl
- Apache模塊 mod_status
- Apache模塊 mod_suexec
- Apache模塊 mod_unique_id
- Apache模塊 mod_userdir
- Apache模塊 mod_usertrack
- Apache模塊 mod_version
- Apache模塊 mod_vhost_alias
- Developer Documentation for Apache 2.0
- Apache 1.3 API notes
- Debugging Memory Allocation in APR
- Documenting Apache 2.0
- Apache 2.0 Hook Functions
- Converting Modules from Apache 1.3 to Apache 2.0
- Request Processing in Apache 2.0
- How filters work in Apache 2.0
- Apache 2.0 Thread Safety Issues
- 詞匯和索引
- 詞匯表
- 指令索引
- 指令速查
- 模塊索引
- 站點導航