Python CGI編程
CGI是什麼?
-
通用網關接口或CGI,是一組定義信息如何在Web服務器和自定義腳本之間交換的標準。
-
CGI規範目前保持是由NCSA 和 NCSA 維護和定義如下。
-
通用網關接口或CGI,是外部網關方案,如HTTP服務器的信息服務器的接口標準。
-
目前的版本是CGI/1.1,而CGI/1.2目前正在定製中。
網頁瀏覽
要了解CGI的概念,讓我們看看當點擊一個超鏈接,瀏覽某一個網頁或URL發生什麼情況。
-
瀏覽器觸發HTTP Web服務器和網址也就是文件名的請求。
-
Web服務器將解析URL,並尋找,如果找到該文件,然後將其發送回瀏覽器中的文件名,否則將表示請求了錯誤文件的錯誤消息。
-
Web瀏覽器需要從Web服務器和顯示器接收到的文件信息或錯誤信息的響應。
然而,也可以設置在HTTP服務器,以便每當在某個目錄中的文件被請求文件不被發送回;相反,它被執行的程序,無論該程序的輸出被發送回瀏覽器顯示。此函數被稱為公共網關接口CGI或與程序稱為CGI腳本。這些CGI程序可以是一個Python腳本,Perl腳本,Shell腳本,C或C++程序等等。
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程序
第一個CGI程序
下麵是一個簡單的鏈接,鏈接到一個名為hello.py CGI腳本。此文件被保存在/var/www/cgi-bin目錄,它有以下內容。運行CGI程序之前,請確保有使用chmod 755 hello.py UNIX命令,使文件可執行文件的更改模式。
#!/usr/bin/python print "Content-type:text/html " print '<html>' print '<head>' print '<title>Hello Word - First CGI Program</title>' print '</head>' print '<body>' print '<h2>Hello Word! This is my first CGI program</h2>' print '</body>' print '</html>'
如果單擊hello.py,那麼這將產生以下的輸出:
Hello Word! This is my first CGI program |
hello.py腳本是一個簡單的Python腳本,它在標準輸出(即屏幕)輸出顯示文件。還有一個重要的額外功能,第一行要打印Content-type:text/html 。這行內容被發送回瀏覽器和指定內容類型可以在瀏覽器畫麵上顯示出來。
現在,必須了解CGI的基本概念,可以使用Python編寫許多複雜的CGI程序。腳本可以與任何其他外部係統交互還向交換信息,如RDBMS(數據庫管理係統)。
HTTP報頭
第一行Content-type:text/html 被發送到瀏覽器,了解內容的HTTP標頭的一部分。所有HTTP報頭將在下麵的表格:
HTTP Field Name: Field Content For Example Content-type: text/html
還有其他一些重要的HTTP頭,會在CGI程序經常使用。
報頭 | 描述 |
---|---|
Content-type: | 返回MIME字符串,定義文件的格式。例如Content-type:text/html |
Expires: Date | 日期的信息變為無效。這應該被使用的瀏覽器,以決定當一個頁麵需要刷新。有效日期字符串應該是格式1998年1月1日12:00:00 GMT。 |
Location: URL | 應該返回替代請求URL的URL。可以使用此字段來重定向請求到任何文件。 |
Last-modified: Date | 資源的最後修改的日期。 |
Content-length: N | 數據的長度,以字節為單位被返回。瀏覽器使用這個值來報告預計下載時間的文件。 |
Set-Cookie: String | 通過設置該字符串傳遞cookie |
CGI環境變量
所有CGI程序將可使用下麵的環境變量。在編寫任何CGI程序,這些變量中發揮重要作用。
Variable Name | 描述 |
---|---|
CONTENT_TYPE | 內容的數據類型。當客戶端發送內容附加到服務器使用。例如,文件上傳等。 |
CONTENT_LENGTH | 查詢信息的長度。它僅適用於POST請求。 |
HTTP_COOKIE | 返回鍵和值對的形式設置Cookie。 |
HTTP_USER_AGENT | 用戶代理請求頭字段包含有關用戶代理發起請求信息。網絡瀏覽器的名稱。 |
PATH_INFO | TCGI腳本的路徑。 |
QUERY_STRING | 被發送GET方法請求URL編碼的信息。 |
REMOTE_ADDR | 遠程主機發出請求的IP地址。這可以是用於記錄或用於認證的目的是有用的。 |
REMOTE_HOST | 發出請求的主機的完全合格的名稱。如果該信息不可用,則REMOTE_ADDR可用於獲得IP地址。 |
REQUEST_METHOD | 該方法用於製造要求。最常用的方法是GET和POST。 |
SCRIPT_FILENAME | CGI腳本的完整路徑。 |
SCRIPT_NAME | CGI腳本的名稱。 |
SERVER_NAME | 服務器的主機名或IP地址 |
SERVER_SOFTWARE | 軟件服務器運行的名稱和版本。 |
這裡是小CGI程序,列出所有的CGI變量,代碼內容詳細如下:
#!/usr/bin/python import os print "Content-type: text/html "; print "<font size=+1>Environment</font><r>"; for param in os.environ.keys(): print "<b>%20s</b>: %s<r>" % (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服務器和它所產生出現在瀏覽器的位置,如果是很長的字符串,或如果有密碼或其他敏感信息傳遞給服務器,切勿使用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 " 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>"
這將產生以下結果:
Hello ZARA ALI |
簡單的表單示例:GET方法
下麵是一個簡單的例子,通過使用HTML表單和提交按鈕兩個值。我們將使用相同的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 " 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表單兩個值和提交按鈕。我們將使用相同的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 " 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 " 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>"
Passing Text Area Data to CGI Program
TEXTAREA element is used when multiline text has to be passed to the CGI Program.
Here is example HTML code for a form with a TEXTAREA box:
<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> |
The result of this code is the following form:
Below is textarea.cgi script to handle input given by web browser:
#!/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 " 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>"
Passing Drop Down Box Data to CGI Program
Drop Down Box is used when we have many options available but only one or two will be selected.
Here is example HTML code for a form with one drop down box:
<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>
The result of this code is the following form:
Below is dropdown.py script to handle input given by web browser.
#!/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 " 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>"
Using Cookies in CGI
HTTP protocol is a stateless protocol. But for a commercial website, it is required to maintain session information among different pages. For example, one user registration ends after completing many pages. But how to maintain user's session information across all the web pages.
In many situations, using cookies is the most efficient method of remembering and tracking preferences, purchases, commissions, and other information required for better visitor experience or site statistics.
How It Works?
Your server sends some data to the visitor's browser in the form of a cookie. The browser may accept the cookie. If it does, it is stored as a plain text record on the visitor's hard drive. Now, when the visitor arrives at another page on your site, the cookie is available for retrieval. Once retrieved, your server knows/remembers what was stored.
Cookies are a plain text data record of 5 variable-length fields:
-
Expires : The date the cookie will expire. If this is blank, the cookie will expire when the visitor quits the browser.
-
Domain : The domain name of your site.
-
Path : The path to the directory or web page that sets the cookie. This may be blank if you want to retrieve the cookie from any directory or page.
-
Secure : If this field contains the word "secure", then the cookie may only be retrieved with a secure server. If this field is blank, no such restriction exists.
-
Name=Value : Cookies are set and retrieved in the form of key and value pairs.
Setting up Cookies
It is very easy to send cookies to browser. These cookies will be sent along with HTTP Header before to Content-type field. Assuming you want to set UserID and Password as cookies. So cookies setting will be done as follows:
#!/usr/bin/python print "Set-Cookie:UserID=XYZ; " print "Set-Cookie:Password=XYZ123; " print "Set-Cookie:Expires=Tuesday, 31-Dec-2007 23:12:40 GMT"; " print "Set-Cookie:Domain=www.yiibai.com; " print "Set-Cookie:Path=/perl; " print "Content-type:text/html " ...........Rest of the HTML Content....
From this example, you must have understood how to set cookies. We use Set-Cookie HTTP header to set cookies.
Here, it is optional to set cookies attributes like Expires, Domain and Path. It is notable that cookies are set before sending magic line "Content-type:text/html .
Retrieving Cookies
It is very easy to retrieve all the set cookies. Cookies are stored in CGI environment variable HTTP_COOKIE and they will have following form:
key1=value1;key2=value2;key3=value3....
下麵是如何獲取cookies的例子。
#!/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。文件類型的input標簽將創建一個“瀏覽”按鈕。
<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 <html> <body> <p>%s</p> </body> </html> """ % (message,)
如果運行是上Unix/Linux上的腳本,那麼就必須照顧替換文件分隔符,如下所示,否則在Windows機器上麵open()語句應該正常工作。
fn = os.path.basename(fileitem.filename.replace("\", "/" ))
如何彈出“文件下載”對話框?
有時,期望要提供選項當用戶將點擊一個鏈接,它將一個“文件下載”對話框彈出給用戶,而不是顯示的實際內容。這是很容易的,並通過HTTP頭來實現。這個HTTP頭跟從上一節提到的報頭是一樣。
例如,如果想從一個給定鏈路進行文件名的文件下載,它的語法如下:
#!/usr/bin/python # HTTP Header print "Content-Type:application/octet-stream; name="FileName" "; print "Content-Disposition: attachment; filename="FileName" "; # Actual File Content will go hear. fo = open("foo.txt", "rb") str = fo.read(); print str # Close opend file fo.close()