# 3.4.1 CGI
### 3.4.1 CGI
CGI即**通用網關接口(Common Gateway Interface)**,1993年由美國NCSA(National Center for Supercomputing Applications)發明。它具有簡單易用、語言無關的特點。雖然今天已經少有人直接使用CGI進行編程,但它仍被主流的Web服務器,如Apache、IIS、Nginx[1](#fn_1)等,所廣泛支持。另外,它還影響了其他一些服務器端技術,如PHP、Python WSGI、Ruby Rack等——在這些技術里你都能看到CGI的影子。所以它仍然值得學習。
先來看一個最簡單的CGI程序[2](#fn_2):
```
#!/usr/bin/perl
print "Content-type: text/plain\n", "\n";
print "Hello, CGI!";
```
把這個程序保存到文件hello.pl,并假設它對應的URL是`http://localhost/cgi-bin/hello.pl`。在瀏覽器中訪問這個URL,你就能看到程序的輸出結果——一個普通文本:
```
Hello, CGI!
```
在前面[HTTP - 服務器應答](response.html)一節我們已經了解了HTTP應答的格式,對照一下,你會發現這個CGI程序的輸出正是如此:首先是若干頭部(header),一個一行(這里僅有一個Content-Type頭);接著是一個空行(注意Content-Type這一行結尾輸出了兩個”\\n”:第一個是頭部的換行,第二個是分隔頭部和正文的空行);最后是消息正文。與之前不同的是,這里我們沒有輸出應答狀態行(Status Line)——這有一些特別:如果CGI程序沒有指明,缺省的狀態代碼是200;如果想返回其他代碼,需要用到一個特殊的Status頭部(header),如:
```
print "Status: 404 Not found\n";
```
CGI程序可以動態產生任何內容,只要按照HTTP應答的格式向標準輸出(stdout)設備輸出這些內容即可。另外,CGI程序也可以由任何語言來編寫,上面的例子只是以Perl為例,你還可以用Python、Ruby、BASH腳本……,以及編譯好的C/C++程序。
在上面的例子中我們還沒有談到程序的輸入(input)。CGI程序使用環境變量作為輸入。下面的例子展示了這一點:
```
#!/usr/bin/perl
print "Content-type: text/plain\n", "\n";
foreach my $key (sort keys %ENV) {
print "$key => $ENV{$key}\n";
}
```
這個CGI程序把它的環境變量和值都打印了出來。假設它對應的URL是`http://localhost/cgi-bin/env.pl`。我們在瀏覽器中訪問`http://localhost/cgi-bin/env.pl?name=Bob`,會得到類似如下的結果:
```
GATEWAY_INTERFACE => CGI/1.1
HTTP_ACCEPT => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
HTTP_ACCEPT_ENCODING => gzip, deflate, sdch
HTTP_ACCEPT_LANGUAGE => zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4
HTTP_CACHE_CONTROL => max-age=0
HTTP_CONNECTION => keep-alive
HTTP_COOKIE => ...
HTTP_HOST => localhost
HTTP_UPGRADE_INSECURE_REQUESTS => 1
HTTP_USER_AGENT => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36
PATH => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
QUERY_STRING => name=Bob
REMOTE_ADDR => 10.0.0.9
REMOTE_PORT => 50497
REQUEST_METHOD => GET
REQUEST_SCHEME => http
REQUEST_URI => /cgi-bin/env.pl?name=Bob
SCRIPT_FILENAME => /var/www/cgi-bin/env.pl
SCRIPT_NAME => /cgi-bin/env.pl
SERVER_ADDR => 10.0.0.8
SERVER_ADMIN => webmaster@localhost
SERVER_NAME => localhost
SERVER_PORT => 80
SERVER_PROTOCOL => HTTP/1.1
SERVER_SIGNATURE => <address>Apache/2.4.7 (Ubuntu) Server at ubuntu-vm Port 80</address>
SERVER_SOFTWARE => Apache/2.4.7 (Ubuntu)
...
```
其中,HTTP\_開頭的變量都是請求頭(request header),其他大部分都是服務器提供的元變量(meta variable),如SERVER\_NAME,REQUEST\_URI和REMOTE\_ADDR等,以及少量關于主機的環境變量,如PATH。這些變量的含義大都不言自明,在此不一一解釋。值得指出的是,我們在URL請求里的查詢部分,即”?name=Bob”,可以通過QUERY\_STRING變量得到。此外,如果請求含有消息正文(message body),如前面“資源與方法”一節的POST的例子,我們可以通過讀取標準輸入設備(stdin)來得到它。
總之,CGI程序通過環境變量和標準輸入獲得請求的各種參數信息,通過標準輸出返回應答;服務器并不關心CGI程序是用什么語言編寫的,它僅通過環境變量和標準輸入、輸出與CGI程序交互。
每當有一個請求對應到一個CGI程序時,服務器就啟動一個進程執行這個CGI程序。因此CGI程序對主機的資源消耗比較大(想想如果有1000個并發請求會怎么樣),同時它的響應速度也會比較慢(進程的啟動比較花時間)。所以人們開始尋找CGI的替代者,這導致了FastCGI等技術的出現。關于CGI的更多信息,可參考[https://en.wikipedia.org/wiki/Common\_Gateway\_Interface](https://en.wikipedia.org/wiki/Common_Gateway_Interface)。
> 1. 嚴格來說Nginx不直接支持CGI,但它支持CGI的變體FastCGI,所以實際上仍然算是支持的。參考:<https://www.nginx.com/resources/wiki/start/topics/examples/simplecgi/>[?](#reffn_1 "Jump back to footnote [1] in the text.")
> 2. 以Apache服務器為例,請參考它的文檔來設置好CGI的運行環境: <http://httpd.apache.org/docs/current/howto/cgi.html>[?](#reffn_2 "Jump back to footnote [2] in the text.")
- 前言
- 1 Web概述
- 1.1 什么是Web
- 1.2 超文本和超鏈接
- 1.3 URL
- 1.4 DNS
- 1.5 HTTP
- 1.5.1 客戶端請求
- 1.5.2 服務器應答
- 1.5.3 進一步了解HTTP
- 1.6 HTTPS
- 2 Web瀏覽器
- 2.1 HTML
- 2.1.1 文檔類型聲明
- 2.1.2 標簽和屬性
- 2.1.3 文檔結構
- 2.1.4 DOM
- 2.1.5 進一步了解HTML
- 2.2 CSS
- 2.2.1 樣式與樣式表
- 2.2.2 樣式表語法
- 2.2.3 級聯樣式表
- 2.2.4 進一步了解CSS
- 2.3 JavaScript
- 2.3.1 script標簽
- 2.3.2 操縱DOM
- 2.3.3 jQuery
- 2.3.4 進一步了解JavaScript
- 2.4 Ajax
- 2.5 移動設備與響應式Web設計
- 3 Web服務器
- 3.1 方法與資源
- 3.2 狀態代碼
- 3.3 靜態內容與動態內容
- 3.4 編程語言與技術
- 3.4.1 CGI
- 3.4.2 PHP
- 3.4.3 Java
- 3.4.4 Python
- 3.4.5 Ruby
- 3.4.6 Node.js
- 3.5 RESTful Web API
- 3.6 服務器架構
- 3.7 Web緩存
- 3.8 服務器推送
- 4 數據庫
- 4.1 關系型數據庫
- 4.2 NoSQL數據庫
- 5 Web服務器的其他組件
- 5.1 Cron
- 5.2 消息隊列
- 5.3 郵件服務器
- 6 開發工具與技術
- 6.1 Git
- 6.1.1 Git基礎操作
- 6.1.2 Git基本原理
- 6.1.3 進一步了解Git
- 6.2 敏捷開發