Python CGI編程

cgi.FieldStorage() 訪問做爲web請求一部分發送給web服務器的數據,數據做爲一個python字典。

CGI是什麼?

  • 通用網關接口或CGI,是一組定義信息如何在Web服務器和自定義腳本之間交換的標準。html

  • CGI規範目前保持是由NCSA 和 NCSA 維護和定義以下。python

  • 通用網關接口或CGI,是外部網關方案,如HTTP服務器的信息服務器的接口標準。git

  • 目前的版本是CGI/1.1,而CGI/1.2目前正在定製中。web

網頁瀏覽

要了解CGI的概念,讓咱們看看當點擊一個超連接,瀏覽某一個網頁或URL發生什麼狀況。數據庫

  • 瀏覽器觸發HTTP Web服務器和網址也就是文件名的請求。編程

  • Web服務器將解析URL,並尋找,若是找到該文件,而後將其發送回瀏覽器中的文件名,不然將表示請求了錯誤文件的錯誤消息。瀏覽器

  • Web瀏覽器須要從Web服務器和顯示器接收到的文件信息或錯誤信息的響應。服務器

然而,也能夠設置在HTTP服務器,以便每當在某個目錄中的文件被請求文件不被髮送回;相反,它被執行的程序,不管該程序的輸出被髮送回瀏覽器顯示。此函 數被稱爲公共網關接口CGI或與程序稱爲CGI腳本。這些CGI程序能夠是一個Python腳本,Perl腳本,Shell腳本,C或C++程序等等。cookie

CGI架構圖

CGI Architecture

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>

下面是上述形式的實際輸出,輸入您名字和姓氏,而後單擊提交按鈕來查看結果。

First Name: 
Last Name:   
 

使用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>

這裏是在上述形式的實際輸出。輸入名字和姓氏,而後單擊提交按鈕來查看結果。

First Name: 
Last Name:   
 

傳遞複選框數據給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>

這段代碼的結果以下表:

 Maths   Physics 
 

下面是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>

這段代碼的結果以下表:

 Maths   Physics 
 

下面是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>

這段代碼的結果以下表:

File: 

 

上面的例子中編寫完成後,能夠在你的服務器嘗試上面的代碼。

下面是腳本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()
相關文章
相關標籤/搜索