通用網關接口 (CGI) 是一組標準,用于定義 Web 服務器和自定義腳本之間如何交換信息。CGI 規范目前由 NCSA 維護。
什么是 CGI?
通用網關接口 (CGI) 是外部網關程序與 HTTP 服務器等信息服務器接口的標準。
當前版本是 CGI/1.1,CGI/1.2 正在開發中。
網頁瀏覽
為了理解 CGI 的概念,讓我們看看當我們點擊一個超鏈接來瀏覽一個特定的網頁或 URL 時會發生什么。
您的瀏覽器聯系 HTTP Web 服務器并要求提供 URL,即文件名。
Web 服務器解析 URL 并查找文件名。如果它找到該文件,則將其發送回瀏覽器,否則發送一條錯誤消息,指示您請求了錯誤的文件。
Web 瀏覽器從 Web 服務器獲取響應并顯示接收到的文件或錯誤消息。
但是,可以設置 HTTP 服務器,以便無論何時請求某個目錄中的文件,該文件都不會被發回;相反,它作為一個程序執行,并且該程序輸出的任何內容都會發送回您的瀏覽器以顯示。此功能稱為通用網關接口或 CGI,程序稱為 CGI 腳本。這些 CGI 程序可以是 Python 腳本、PERL 腳本、Shell 腳本、C 或 C++ 程序等。
CGI 架構圖
CGI 架構
Web 服務器支持和配置
在繼續進行 CGI 編程之前,請確保您的 Web 服務器支持 CGI 并且已配置為處理 CGI 程序。HTTP 服務器要執行的所有 CGI 程序都保存在預先配置的目錄中。這個目錄被稱為 CGI 目錄,按照慣例,它被命名為 /var/www/cgi-bin。按照慣例,CGI 文件的擴展名為。cgi,但您也可以使用 python 擴展名.py保存文件。
默認情況下,Linux 服務器配置為僅運行 /var/www 中 cgi-bin 目錄中的腳本。如果要指定任何其他目錄來運行 CGI 腳本,請在 httpd.conf 文件中注釋以下行 -
<Directory "/var/www/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
<Directory "/var/www/cgi-bin">
Options All
</Directory>
在這里,我們假設您已經成功啟動并運行了 Web 服務器,并且您能夠運行任何其他 CGI 程序,如 Perl 或 Shell 等。
第一個 CGI 程序
這是一個簡單的鏈接,它鏈接到一個名為hello.py的 CGI 腳本。該文件保存在 /var/www/cgi-bin 目錄下,內容如下。在運行 CGI 程序之前,請確保您已使用chmod 755 hello.py UNIX 命令更改文件模式以使文件可執行。
#!/usr/bin/python
print "Content-type:text/html\r\n\r\n"
print '<html>'
print '<head>'
print '<title>Hello World - First CGI Program</title>'
print '</head>'
print '<body>'
print '<h2>Hello World! This is my first CGI program</h2>'
print '</body>'
print '</html>'
如果單擊 hello.py,則會產生以下輸出 -
你好世界!這是我的第一個 CGI 程序
這個 hello.py 腳本是一個簡單的 Python 腳本,它將其輸出寫入 STDOUT 文件,即屏幕。有一個重要且額外的功能可用,即要打印的第一行Content-type:text/html\r\n\r\n。該行被發送回瀏覽器,它指定要在瀏覽器屏幕上顯示的內容類型。
至此,您一定已經了解了 CGI 的基本概念,并且可以使用 Python 編寫許多復雜的 CGI 程序。該腳本還可以與任何其他外部系統交互以交換信息,例如 RDBMS。
HTTP 標頭
行Content-type:text/html\r\n\r\n是 HTTP 標頭的一部分,發送到瀏覽器以了解內容。所有 HTTP 標頭都將采用以下形式 -
HTTP Field Name: Field Content
For Example
Content-type: text/html\r\n\r\n
很少有其他重要的 HTTP 標頭,您將在 CGI 編程中經常使用它們。
編號 標題和說明
1
內容類型:
定義返回文件格式的 MIME 字符串。示例是 Content-type:text/html
2
過期:日期
信息失效的日期。瀏覽器使用它來決定何時需要刷新頁面。有效日期字符串的格式為 01 Jan 1998 12:00:00 GMT。
3
地點:網址
返回的 URL,而不是請求的 URL。您可以使用此字段將請求重定向到任何文件。
4
最后修改:日期
上次修改資源的日期。
5
內容長度:N
返回的數據的長度(以字節為單位)。瀏覽器使用此值報告文件的估計下載時間。
6
設置 Cookie:字符串
設置通過字符串傳遞的cookie
CGI 環境變量
所有 CGI 程序都可以訪問以下環境變量。這些變量在編寫任何 CGI 程序時都起著重要作用。
編號 變量名稱和描述
1
內容類型
內容的數據類型。當客戶端向服務器發送附加內容時使用。例如,文件上傳。
2
CONTENT_LENGTH
查詢信息的長度。它僅適用于 POST 請求。
3
HTTP_COOKIE
以鍵值對的形式返回設置的 cookie。
4
HTTP_USER_AGENT
User-Agent request-header 字段包含有關發起請求的用戶代理的信息。它是 Web 瀏覽器的名稱。
5
PATH_INFO
CGI 腳本的路徑。
6
請求參數
使用 GET 方法請求發送的 URL 編碼信息。
7
遠程地址
發出請求的遠程主機的 IP 地址。這對于日志記錄或身份驗證很有用。
8
遠程主機
發出請求的主機的完全限定名稱。如果此信息不可用,則可以使用 REMOTE_ADDR 獲取 IR 地址。
9
REQUEST_METHOD
用于發出請求的方法。最常用的方法是 GET 和 POST。
10
SCRIPT_FILENAME
CGI 腳本的完整路徑。
11
SCRIPT_NAME
CGI 腳本的名稱。
12
服務器名稱
服務器的主機名或 IP 地址
13
服務器軟件
服務器正在運行的軟件的名稱和版本。
這是列出所有 CGI 變量的小型 CGI 程序。單擊此鏈接查看結果獲取環境
#!/usr/bin/python
import os
print "Content-type: text/html\r\n\r\n";
print "<font size=+1>Environment</font><\br>";
for param in os.environ.keys():
print "<b>%20s</b>: %s<\br>" % (param, os.environ[param])
GET 和 POST 方法
當您需要將一些信息從瀏覽器傳遞到 Web 服務器并最終傳遞到 CGI 程序時,您一定遇到過很多情況。最常見的是,瀏覽器使用兩種方法將這些信息傳遞給 Web 服務器。這些方法是 GET 方法和 POST 方法。
使用 GET 方法傳遞信息
GET 方法發送附加到頁面請求的編碼用戶信息。頁面和編碼信息由 ? 分隔。字符如下 -
http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
GET 方法是將信息從瀏覽器傳遞到 Web 服務器的默認方法,它會生成一個長字符串,該字符串會出現在瀏覽器的 Location:box 中。如果您有密碼或其他敏感信息要傳遞給服務器,切勿使用 GET 方法。GET 方法有大小限制:請求字符串中只能發送 1024 個字符。GET 方法使用 QUERY_STRING 標頭發送信息,并且可以通過 QUERY_STRING 環境變量在您的 CGI 程序中訪問。
您可以通過簡單地連接鍵和值對以及任何 URL 來傳遞信息,或者您可以使用 HTML <FORM> 標記使用 GET 方法傳遞信息。
簡單的 URL 示例:Get 方法
這是一個簡單的 URL,它使用 GET 方法將兩個值傳遞給 hello_get.py 程序。
/cgi-bin/hello_get.py?first_name=ZARA&last_name=ALI
下面是hello_get.py腳本,用于處理 Web 瀏覽器給出的輸入。我們將使用cgi模塊,這使得訪問傳遞的信息變得非常容易 -
#!/usr/bin/python
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
first_name = form.getvalue('first_name')
last_name = form.getvalue('last_name')
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Hello - Second CGI Program</title>"
print "</head>"
print "<body>"
print "<h2>Hello %s %s</h2>" % (first_name, last_name)
print "</body>"
print "</html>"
這將產生以下結果 -
你好 ZARA 阿里
簡單的FORM例子:GET方法
此示例使用 HTML FORM 和提交按鈕傳遞兩個值。我們使用相同的 CGI 腳本 hello_get.py 來處理這個輸入。
<form action = "/cgi-bin/hello_get.py" method = "get">
First Name: <input type = "text" name = "first_name"> <br />
Last Name: <input type = "text" name = "last_name" />
<input type = "submit" value = "Submit" />
</form>
這是上述表單的實際輸出,輸入名字和姓氏,然后單擊提交按鈕以查看結果。
名:
姓:
使用 POST 方法傳遞信息
將信息傳遞給 CGI 程序的通常更可靠的方法是 POST 方法。這以與 GET 方法完全相同的方式打包信息,但不是在 ? 之后將其作為文本字符串發送。在 URL 中,它作為單獨的消息發送。此消息以標準輸入的形式進入 CGI 腳本。
下面是處理 GET 和 POST 方法的相同 hello_get.py 腳本。
#!/usr/bin/python
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
first_name = form.getvalue('first_name')
last_name = form.getvalue('last_name')
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Hello - Second CGI Program</title>"
print "</head>"
print "<body>"
print "<h2>Hello %s %s</h2>" % (first_name, last_name)
print "</body>"
print "</html>"
讓我們再次采用與上面相同的示例,它使用 HTML FORM 和提交按鈕傳遞兩個值。我們使用相同的 CGI 腳本 hello_get.py 來處理這個輸入。
<form action = "/cgi-bin/hello_get.py" method = "post">
First Name: <input type = "text" name = "first_name"><br />
Last Name: <input type = "text" name = "last_name" />
<input type = "submit" value = "Submit" />
</form>
這是上述表格的實際輸出。您輸入名字和姓氏,然后單擊提交按鈕以查看結果。
名:
姓:
將復選框數據傳遞給 CGI 程序
當需要選擇多個選項時使用復選框。
這是帶有兩個復選框的表單的示例 HTML 代碼 -
<form action = "/cgi-bin/checkbox.cgi" method = "POST" target = "_blank">
<input type = "checkbox" name = "maths" value = "on" /> Maths
<input type = "checkbox" name = "physics" value = "on" /> Physics
<input type = "submit" value = "Select Subject" />
</form>
此代碼的結果如下形式 -
數學 物理
下面是 checkbox.cgi 腳本,用于處理 Web 瀏覽器為復選框按鈕提供的輸入。
#!/usr/bin/python
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
if form.getvalue('maths'):
math_flag = "ON"
else:
math_flag = "OFF"
if form.getvalue('physics'):
physics_flag = "ON"
else:
physics_flag = "OFF"
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Checkbox - Third CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> CheckBox Maths is : %s</h2>" % math_flag
print "<h2> CheckBox Physics is : %s</h2>" % physics_flag
print "</body>"
print "</html>"
將單選按鈕數據傳遞給 CGI 程序
當只需要選擇一個選項時,使用單選按鈕。
這是帶有兩個單選按鈕的表單的示例 HTML 代碼 -
<form action = "/cgi-bin/radiobutton.py" method = "post" target = "_blank">
<input type = "radio" name = "subject" value = "maths" /> Maths
<input type = "radio" name = "subject" value = "physics" /> Physics
<input type = "submit" value = "Select Subject" />
</form>
此代碼的結果如下形式 -
數學 物理
下面是 radiobutton.py 腳本,用于處理 Web 瀏覽器為單選按鈕提供的輸入 -
#!/usr/bin/python
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
if form.getvalue('subject'):
subject = form.getvalue('subject')
else:
subject = "Not set"
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Radio - Fourth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Selected Subject is %s</h2>" % subject
print "</body>"
print "</html>"
將文本區域數據傳遞給 CGI 程序
當必須將多行文本傳遞給 CGI 程序時,使用 TEXTAREA 元素。
這是帶有 TEXTAREA 框的表單的示例 HTML 代碼 -
<form action = "/cgi-bin/textarea.py" method = "post" target = "_blank">
<textarea name = "textcontent" cols = "40" rows = "4">
Type your text here...
</textarea>
<input type = "submit" value = "Submit" />
</form>
此代碼的結果如下形式 -
Type your text here...
下面是處理網絡瀏覽器輸入的 textarea.cgi 腳本 -
#!/usr/bin/python
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
if form.getvalue('textcontent'):
text_content = form.getvalue('textcontent')
else:
text_content = "Not entered"
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>";
print "<title>Text Area - Fifth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Entered Text Content is %s</h2>" % text_content
print "</body>"
將下拉框數據傳遞給 CGI 程序
當我們有許多可用選項但只選擇一兩個時使用下拉框。
這是帶有一個下拉框的表單的示例 HTML 代碼 -
<form action = "/cgi-bin/dropdown.py" method = "post" target = "_blank">
<select name = "dropdown">
<option value = "Maths" selected>Maths</option>
<option value = "Physics">Physics</option>
</select>
<input type = "submit" value = "Submit"/>
</form>
此代碼的結果如下形式 -
數學
下面是處理 Web 瀏覽器輸入的 dropdown.py 腳本。
#!/usr/bin/python
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
if form.getvalue('dropdown'):
subject = form.getvalue('dropdown')
else:
subject = "Not entered"
print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Dropdown Box - Sixth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Selected Subject is %s</h2>" % subject
print "</body>"
print "</html>"
在 CGI 中使用 Cookie
HTTP 協議是一種無狀態協議。對于商業網站,需要維護不同頁面之間的會話信息。例如,一個用戶注冊在完成許多頁面后結束。如何跨所有網頁維護用戶的會話信息?
在許多情況下,使用 cookie 是記住和跟蹤偏好、購買、傭金和其他更好的訪問者體驗或網站統計所需的信息的最有效方法。
這個怎么運作?
您的服務器以 cookie 的形式向訪問者的瀏覽器發送一些數據。瀏覽器可以接受 cookie。如果是這樣,它將作為純文本記錄存儲在訪問者的硬盤上?,F在,當訪問者到達您網站上的另一個頁面時,cookie 就可以檢索了。檢索后,您的服務器知道/記住存儲的內容。
Cookie 是 5 個可變長度字段的純文本數據記錄 -
Expires - cookie 過期的日期。如果此項為空,則 cookie 將在訪問者退出瀏覽器時過期。
Domain - 您網站的域名。
Path - 設置 cookie 的目錄或網頁的路徑。如果您想從任何目錄或頁面檢索 cookie,這可能是空白的。
Secure - 如果此字段包含“安全”一詞,則只能使用安全服務器檢索 cookie。如果此字段為空,則不存在此類限制。
Name=Value - 以鍵值對的形式設置和檢索 Cookie。
設置 Cookie
將 cookie 發送到瀏覽器非常容易。這些 cookie 與 HTTP Header 一起發送到 Content-type 字段。假設您要將 UserID 和 Password 設置為 cookie。設置 cookie 如下:
#!/usr/bin/python
print "Set-Cookie:UserID = XYZ;\r\n"
print "Set-Cookie:Password = XYZ123;\r\n"
print "Set-Cookie:Expires = Tuesday, 31-Dec-2007 23:12:40 GMT";\r\n"
print "Set-Cookie:Domain = www.tutorialspoint.com;\r\n"
print "Set-Cookie:Path = /perl;\n"
print "Content-type:text/html\r\n\r\n"
...........Rest of the HTML Content....
從這個例子中,您一定已經了解了如何設置 cookie。我們使用Set-Cookie HTTP 標頭來設置 cookie。
設置 cookie 屬性(如 Expires、Domain 和 Path)是可選的。值得注意的是,cookie 是在發送魔法行"Content-type:text/html\r\n\r\n之前設置的。
檢索 Cookie
檢索所有設置的 cookie 非常容易。Cookie 存儲在 CGI 環境變量 HTTP_COOKIE 中,它們將具有以下形式 -
key1 = value1;key2 = value2;key3 = value3....
以下是如何檢索 cookie 的示例。
#!/usr/bin/python
# Import modules for CGI handling
from os import environ
import cgi, cgitb
if environ.has_key('HTTP_COOKIE'):
for cookie in map(strip, split(environ['HTTP_COOKIE'], ';')):
(key, value ) = split(cookie, '=');
if key == "UserID":
user_id = value
if key == "Password":
password = value
print "User ID = %s" % user_id
print "Password = %s" % password
這將為上述腳本設置的 cookie 產生以下結果 -
User ID = XYZ
Password = XYZ123
文件上傳示例
要上傳文件,HTML 表單必須將 enctype 屬性設置為multipart/form-data。具有文件類型的輸入標簽會創建一個“瀏覽”按鈕。
<html>
<body>
<form enctype = "multipart/form-data"
action = "save_file.py" method = "post">
<p>File: <input type = "file" name = "filename" /></p>
<p><input type = "submit" value = "Upload" /></p>
</form>
</body>
</html>
此代碼的結果如下形式 -
文件:未選擇任何文件
上面的示例已被故意禁用以保存人們在我們的服務器上上傳文件,但您可以在您的服務器上嘗試上面的代碼。
這是處理文件上傳的腳本save_file.py -
#!/usr/bin/python
import cgi, os
import cgitb; cgitb.enable()
form = cgi.FieldStorage()
# Get filename here.
fileitem = form['filename']
# Test if the file was uploaded
if fileitem.filename:
# strip leading path from file name to avoid
# directory traversal attacks
fn = os.path.basename(fileitem.filename)
open('/tmp/' + fn, 'wb').write(fileitem.file.read())
message = 'The file "' + fn + '" was uploaded successfully'
else:
message = 'No file was uploaded'
print """\
Content-Type: text/html\n
<html>
<body>
<p>%s</p>
</body>
</html>
""" % (message,)
如果您在 Unix/Linux 上運行上述腳本,則需要注意替換文件分隔符,如下所示,否則在您的 windows 機器上,上面的 open() 語句應該可以正常工作。
fn = os.path.basename(fileitem.filename.replace("\\", "/" ))
如何彈出“文件下載”對話框?
有時,您希望提供用戶可以單擊鏈接的選項,它會向用戶彈出“文件下載”對話框,而不是顯示實際內容。這很容易,可以通過 HTTP 標頭來實現。此 HTTP 標頭與上一節中提到的標頭不同。
例如,如果您想從給定鏈接下載FileName文件,則其語法如下 -
#!/usr/bin/python
# HTTP Header
print "Content-Type:application/octet-stream; name = \"FileName\"\r\n";
print "Content-Disposition: attachment; filename = \"FileName\"\r\n\n";
# Actual File Content will go here.
fo = open("foo.txt", "rb")
str = fo.read();
print str
# Close opend file
fo.close()
希望您喜歡本教程。如果是,請將您的反饋發送給我:聯系我們