天天看點

【轉】CGI入門

原文位址:http://www.jdon.com/idea/cgi.htm

 cgi 意思為 common gateway interface, 一種基于浏覽器的輸入、在web伺服器上運作的程式方法. cgi腳本 使你的浏覽器與使用者能互動,為了在資料庫中尋找一個名詞, 提供你寫入的評論,或者從一個表單中選擇幾個條目并且能得到一個明确的回答. 如果你曾經遇到過在web上填表或進行搜尋, 你就是用的cgi腳本. 你那時也許沒有意識到,因為大部分工作是在伺服器上運作的,你看到的隻是結果.

  作為一個網頁設計者, 你建立用戶端的 cgi腳本, 伺服器端的程式用來處理使用者輸入, 結果傳回給使用者.

  在這裡你将學習關于cgi腳本的一切:

cgi腳本是什麼?它是怎樣工作的

一個cgi腳本輸出象什麼?

怎樣用參數或無參數建立一個cgi腳本

怎樣建立一個傳回規定響應的cgi腳本

怎樣建立一個輸入表單的cgi腳本

有關在使用cgi腳本中的問題

你能在腳本中使用的cgi變量

cgi腳本是什麼?

cgi腳本 怎樣工作的?

一個簡單的例子

我能用cgi腳本嗎?

你的伺服器配置允許cgi腳本嗎?

你能程式設計嗎?

你應該用什麼程式設計語言?

你的伺服器設定正确嗎?

如果你的伺服器不是unix系統呢?

解剖cgi腳本

輸出頭部

輸出資料部

帶闡述的腳本

傳遞其他資訊給腳本

建立特殊的腳本輸出

以裝載另一個文本響應

無響應

處理表單的腳本

表單格式和表單腳本

get 和 post

url 編碼

問題

cgi 變量

解碼程式

uncgi

cgi-lib.pl

解碼檔案上傳的輸入

自己做

非解剖的頭部腳本

總結

  cgi腳本簡單地講是個運作在web伺服器上的程式, 有浏覽器的輸入觸發. 這個腳本通常象伺服器和系統中其他程式如資料庫的橋梁。

  cgi 腳本難道不是一個真正的腳本?按照你的伺服器的支援, 他們可能是一個編譯好的程式或者批指令檔案或者其他可執行的東西. 為了簡單起見,我們統稱他們為腳本scripts. 

  cgi 腳本是任何運作在web伺服器上的程式. cgi意思是common gateway interface。

  cgi腳本是用下列兩種方法使用的: 作為一個表單的action 或 作為一個頁中的直接link。

  cgi腳本由伺服器調用, 基于浏覽器的資料輸入. 其工作原理如下:

一個url指向一個cgi腳本. 一個cgi腳本的url能如普通的url一樣出現,差別于.htm/.html靜态ur,cgi的url是動态url。如http://xxxx.com/cgiurl

伺服器cgi接收浏覽器的請求, 按照那個url指向對應的腳本檔案(注意檔案的位置和擴充名),執行cgi腳本.

cgi腳本執行基于輸入資料的操作,包括查詢資料庫、計算數值或調用系統中其他程式.

cgi腳本産生某種web伺服器能了解的輸出結果.

伺服器接收來自腳本的輸出并且把它傳回浏覽器,讓使用者了解處理結果。

  這裡詳細一步一步地解釋所有有關發生的細節。 

  假設有一個html頁面有一個指向cgi腳本的連結:

  這個連結指向的是一個cgi腳本,因為其中有cgi-bin的路徑. 在許多伺服器cgi-bin是僅能夠放置cgi腳本 的目錄。

  當你選擇這個連結時, 你的浏覽器将向www.jdon.com伺服器提出請求. 伺服器接收這個請求計算出url處的腳本檔案名然後執行getdate這個腳本.

  這個getdate腳本, 在unix系統中執行是這樣的:

  第一行是個特殊的指令,告訴unix系統這是個shell腳本; 真正邏輯是從這行開始的下一行,這個腳本做兩件事:它輸出行content-type: text/plain, 接着開始一個空行;第二, 它調用unix系統時間date程式, 這樣輸出日期和時間. 腳本執行後輸出應該這樣:

  這個content-type是什麼東東?它是個特殊的編碼,web伺服器用來告訴浏覽器輸出這個文本是什麼類型的. 這與html中content-type含義是一樣的。

  這是最基本的,實際情況要複雜得多,總之可以用來了解浏覽器、伺服器和腳本之間是怎樣工作的。

  在你使用cgi腳本之前,有兩件事你也許要解決:cgi腳本 是個進階的web特性并且需要你擁有與web 伺服器管理者一樣棒的知識。

  肯定嗎?就是做不到,學學也可以?好吧!讓我們繼續.

  為了能編寫和運作cgi腳本, 你需要一個web伺服器. 不象通常靜态的html檔案, 你不能在本地系統上編寫或試驗你的cgi腳本; 你得通過web伺服器來做這個.

  但是即使你有一個web伺服器, 這個伺服器必須特别地為運作cgi腳本配置一下. 那意味着你所有的腳本必須放置在一個叫做cgi-bin的目錄下.

  在編寫cgi腳本之前, 需要詢問你的伺服器管理者是否允許你安裝和運作cgi腳本, 并且如果可以的話,他們必須放置在哪兒?還有,你必須有個真正的web伺服器,如果是ftp或gopher伺服器,那你就不能用cgi.

  如果你在自己的伺服器上運作, 你必須特别地創造一個叫cgi-bin的目錄,并配置你的伺服器認可這個目錄為一個腳本目錄. 也必須記住下面有關cgi腳本特點:

每個腳本是個程式, 它運作在浏覽器可以請求的系統上, 執行時使用cpu時間和記憶體. 如果有成打上千的這些腳本同時運作,會怎樣?你的系統将不忍負載直至崩潰。

如果你不仔細地編寫你的cgi腳本, 你将有可能讓别人通過你的cgi腳本參數進入傷害你的系統.

  初學者注意! 一般地, 你必須具備一些基本程式設計概念與方法。你必須有類似系統工作的經驗。如果你沒有這些背景, 你必須去學習,好了,費話不多說.

  你可以用你熟悉的任何語言編寫cgi腳本, 隻要你的腳本遵守下一節所陳列的規則即可,隻要那個語言能在你的web伺服器系統上運作.

  在這本學習手冊中,僅用兩種語言編寫cgi腳本: unix shell和 perl語言. 這個shell是适合在任何相近的unix系統上運作并且容易學習, 但是處理複雜的情況就困難了. perl是免費的, 這個語言特點是穩定和強大的,類似c,但它也是較難學習的.

  為了運作任何一個cgi腳本, 不管簡單或複雜的,你的伺服器必須設定成能夠運作他們,必須放置在一個特定的目錄,必須有一個依賴你伺服器設定的檔案擴充名.

  如果你是租用伺服器,就要是否允許運作cgi腳本.

  如果你擁有自己的伺服器,檢查你的伺服器說明書是怎樣處理cgi腳本的.

隻好再找别的學習手冊了。

  如果你編寫它很久,克服很多警告和配置,恭喜你,你已經會些cgi腳本,并且可以在你的網頁使用了. 在這一章,将學習腳本是怎樣執行,你的伺服器又是怎樣與他們對話産生回應的。

  雖然你的cgi腳本可以讓你做任何事情,但是腳本的輸出還是必須有一個規定形式. 

  這個 "腳本輸出" 意思是指你的腳本發回伺服器的資料. 在unix系統中, 輸出是發向标準輸出, 伺服器從那兒檢測它. 在其他系統和伺服器, 你的腳本輸出也許不一樣了. 

  首先,輸出頭部資訊,頭部是實際不是文本的一部分,是伺服器與浏覽器之間的資訊協定,你實際看不到。 有三個類型的頭部: content-type, location, 和status. content-type 是最普遍的。

  有關content-type解釋可以見有關html的說明, 發出text/html特定編碼如下:

  在這個例子中,輸出資料的類型是text/html; 換句話說, 輸出的是個html檔案.

表1. 通用格式和content-types. 

html

text/html

text

text/plain

gif

p_w_picpath/gif

jpeg

p_w_picpath/jpeg

postscript

application/postscript

mpeg

video/mpeg

  注意content-type 後面必須跟一個空行. 如果你沒有空行,伺服器将無法搞清這個頭部在哪裡結束。

  你輸出的資料應該符合你所規定的content-type; 如果content-type是text/html, 輸出安置應該是在html. 如果content-type是p_w_picpath/gif, 輸出應該是在一個二進制的gif檔案.

  這是個簡單的輸出日期的簡單腳本,這個cgi腳本還檢檢視看是否已經登陸到web伺服器,并且報告發現了什麼。

  調用代碼這樣:

  這是沒有輸入的腳本,它隻運作并且傳回資料.

  pinglaura腳本内容是這樣::

#!/bin/sh 

echo content-type: text/html

echo "<html><head>" 

echo "<title>is laura there?</title>" 

echo "</head><body>" 

  為了測試是否已經登陸系統,用who指令(登陸名假設為lemay), 儲存結果在變量ison中. 如果登陸, 變量ison将有些登入内容,否則是空的.

  試驗結果及傳回相應提示的腳本是這樣:

  最後關閉html:

  現在你通過從指令行運作這個腳本,測試一下,你将得到一個結果說xxx未登陸你的系統,輸出是這樣的:

  這是輸出的一個html文本,這樣你的浏覽器能正常顯示這内容,因為它是個html檔案。 

  現在将它copy到你的伺服器的cgi-bin目錄下,就可以從浏覽器調用執行了,如果你不能通路cgi-bin目錄,你必須詢問你的伺服器管理者,你不能理所當然地自己建立個cgi-bin目錄,那沒用的。 

  這個例子完整的腳本如下:

  為了傳遞一個參數給腳本,可以在url中使用 (?) 插入腳本名詞和參數之間, 用加号(+) 表示每個單一的參數, 如:

  當伺服器接收到這個請求,它傳遞 arg1, arg2, 和 arg3 參數給腳本. 你然後能在腳本中使用這些參數. 這個方法有時叫查詢, 因為早期它用在搜尋功能中.

  既然你知道怎樣使用參數,讓我們繼續上面的例子pinglaura,通過修改這個例子我們得到下面這個腳本pinggeneric.

  我們取個不同标題:

  在上面的例子中, 下一步應該是測試我是否登陸,在這裡我們用參數${1}代替我的名字lemay,  ${1}作為第一個參數, ${2}作為第二個, ${3}作為第三個.

  剩下的所有修改如下:

  好了,讓我們修改html頁中的連接配接吧!原來是這樣:

  修改為通用查詢功能後是這樣,比如查詢名字叫john的人是否登陸:

  在你的伺服器上試試,看是否有結果。

  除了上面介紹的方法傳遞資料給腳本以外,還有第二種方法傳遞資訊給cgi腳本. 它叫作路徑資訊path information, 在上面例子的問号後面的參數是因使用者表單的輸入而改變的. 路徑資訊path info用作其他資訊傳遞給腳本,實際上,你可以用它作任何事情. 

  路徑資訊path information是一種不象通常參數腳本那樣頻繁傳遞資訊的方法. 路徑path information通常是指web伺服器上的那些比如配置檔案、臨時檔案或者被腳本因問題調用的檔案等等此類檔案.

  看下面一個路徑資訊path information例子, :

  當腳本運作時,在路徑中的資訊"myscript"将被放置于環境參數path_info. 你能在你的腳本内容中使用這些資訊.

  比如說, 讓我們假設你在多頁上已有多個連接配接到同一個腳本. 你能用這個路徑資訊顯示那個有連結的html檔案名, 這樣, 在你完成處理你的腳本之後, 當你發回一個html檔案時, 你能在這個檔案裡包含一個連結,發回給使用者其剛開始通路的那個起始頁。

  你會在下一章節學到更多路徑資訊:有用的表單和腳本.

  現在你已經學習了諸如輸出資料 如html資料 發給浏覽器解釋顯示的資料. 但是如果你不想把腳本結果作為一個資料流形式發回浏覽器,而是想把一個存在的頁發回,怎麼辦? 如果你隻是要腳本做一些事而不讓任何結果回答給浏覽器,怎麼辦? 不用擔心, 這裡開始解釋這些情況.

  cgi輸出不是非得一個資料流,有時可以響應告訴浏覽器的是在伺服器上的一個頁面檔案,為了發出這個資訊,看下面的例子:

  這個location行用作通常的輸出檔案定位,也就是說,如果你用了location, 你就不必再用象content-type這樣的資料輸出(實際上,你也不能). 正如content-type, 你也必須在這一行後面跟一個空行.

  指向這個檔案的路徑可以是一個url或相對路徑. 所有相對路徑是指相對于腳本所在的位置. 例子中的final.html文本是在目前上一個目錄下docs的目錄下:

  你不能同時使用content-type 和 location兩個輸出. 比如, 如果你想輸出一個标準頁,但是想在這個頁尾加上客戶的内容, 你就得用content-type自行組建這兩個部分. 注意:你可以用腳本指令打開一個當地檔案作為資料直接将之輸出. 

  有時對于一個cgi腳本也許一點沒有輸出. 有時你隻是要從使用者那兒收集點資訊. 你就不用再調用一個新文本, 也不用輸出結果或打開一個存在的檔案. 在浏覽器上的螢幕還是那個樣子.

  很幸運, 這一切很容易. 你隻要輸出下面這個指令即可(後面跟一個空行):

 這個status頭部提供狀态碼給伺服器(并且也給浏覽器). 特别的204将傳遞給浏覽器,如果能識别它,它将什麼也不做. 

  盡管無響應是一個官方http規定的一部分,但也并不是适合所有的浏覽器,也許會産生奇怪的結果,那你要多試驗試試看啦.

  今天,大多數cgi腳本是用來處理表單輸入的. 這個過程大緻象上面說闡述的一樣,但還是有些不同,比如cgi腳本隻要被調用;資料怎樣從伺服器被發向浏覽器.

  記住, 大多數表單有兩個部分: html的表單格式;處理表單資料的cgi腳本。 這個cgi腳本是使用html标簽<form action=..>屬性調用的.

  正如上面所說,由于表單有兩個部分. action屬性包含着處理表單的腳本:

 在這個表單中, 每個輸入區都有一個name的屬性, 用來稱呼表單元素. 當這個表單資料被遞交,你在action中定義的cgi腳本, 這樣這些name和輸入内容被作為一個數字或字元傳遞給腳本.

  表單從浏覽器發給伺服器有兩種方法.  get 和 post.

  我們上面談論的方法,實際是get,它将資料打包放置在環境變量query_string中作為url整體的一部分傳遞給伺服器。

  post做很多類似get同樣的事情, 不同的地方就是它是分離地傳遞資料給腳本. 你的腳本通過标準輸入擷取這些資料. (有些web伺服器是存儲在臨時檔案中.) 這個query_string環境變量将不再設定.

  那你用那個方法呢? post是個安全的方法, 尤其如果你的表單中有很多資料的話. 當你用get, 這個伺服器就配置設定變量query_string給所有的表單資料, 但是這個變量可存儲量是有限的. 換句話說,如果你有很多資料但是你又用get,你會丢失很多資料.

如果你用post, 你可以盡可能多地使用資料, 因為這些資料從來也不配置設定到一個變量裡.

  url 編碼是一種浏覽器用來打包表單輸入的格式. 浏覽器從表單中擷取所有的name和其中的值 ,将他們作為name/value參數編碼, 移去那些不能傳送的字元, 将資料排行等等,這些還取決于你用get還是post?是作為url的一部分或者分離地發給伺服器? 不管哪種情況, 在伺服器端的表單輸入格式樣子象這樣:

  url編碼遵循下列規則:

每對name/value由&符分開.

每對來自表單的name/value由=符分開. 如果使用者沒有輸入值給這個name,那麼這個name還是出現,隻是無值(象這樣 "name=").

任何特殊的字元(就是那些不是簡單的七位ascii,如漢字) 将以百分符%用十六進制編碼. 當然也包括象 =, &, 和 % 這些特殊的字元.

在輸入區中的空格将以加号+顯示.

  因為表單輸入是用這個url編碼傳遞給你的腳本的,在你用這些參數之前必須解碼,因為解碼是個很普遍的工作,可以有很多工具做這個工作 . 你沒有必要自己寫這個解碼程式.

  這裡介紹一個叫uncgi的解碼程式, 你可以從http://www.hyperion.com/~koreth/uncgi.html. 得到原碼,安裝在你自己的cgi-bin目錄下.

  表2 總結了環境變量. 如下

server_name

cgi腳本運作時的主機名和ip位址.

server_software

你的伺服器的類型如: cern/3.0 或 ncsa/1.3.

gateway_interface

運作的cgi版本. 對于unix伺服器, 這是cgi/1.1.

server_protocol

伺服器運作的http協定. 這裡當是http/1.0.

server_port

伺服器運作的tcp口,通常web伺服器是80.

request_method

post 或 get, 取決于你的表單是怎樣遞交的.

http_accept 

浏覽器能直接接收的content-types, 可以有http accept header定義.

http_user_agent

遞交表單的浏覽器的名稱、版本 和其他平台性的附加資訊。

http_referer

遞交表單的文本的 url,不是所有的浏覽器都發出這個資訊,不要依賴它

path_info

附加的路徑資訊, 由浏覽器通過get方法發出.

path_translated

在path_info中系統規定的路徑資訊.

script_name

指向這個cgi腳本的路徑, 是在url中顯示的(如, /cgi-bin/thescript).

query_string

腳本參數或者表單輸入項(如果是用get遞交). query_string 包含url中問号後面的參數.

remote_host

遞交腳本的主機名,這個值不能被設定.

remote_addr

遞交腳本的主機ip位址.

remote_user

遞交腳本的使用者名. 如果伺服器的authentication被激活,這個值可以設定。

remote_ident

如果web伺服器是在ident (一種确認使用者連接配接你的協定)運作, 遞交表單的系統也在運作ident, 這個變量就含有ident傳回值.

content_type

如果表單是用post遞交, 這個值将是 application/x-www-form-urlencoded. 在上載檔案的表單中, content-type 是個 multipart/form-data.

content_length

對于用post遞交的表單, 标準輸入口的位元組數.

  目前有兩個程式: 通用目的的uncgi, 和cgi-lib.pl, 這是個perl庫,用于perl編寫的cgi腳本.當然也有表單上載時可以解碼的程式,不過很少。

  說明原碼可以從 http://www.hyperion.com/~koreth/uncgi.html獲得。

  這是由steve brenner編寫的, 幫助你管理輸入. 他能從get和post擷取輸入并且放置在一個perl清單或陣列中. 更新的版本也能處理來自表單的檔案上傳. 從這兒可以得到資訊與原碼http://www.bio.cam.ac.uk/cgi-lib. 如果你決定用perl語言處理你的表單輸入,cgi-lib是個很好的庫.

  為了使用cgi-lib.pl,你通常要這樣寫: 

#!/usr/lib/perl

  cgi-lib中盡管有很多子程式, 最重要的是readparse子程. readparse 讀取輸入友善地将name/value儲存在一個perl陣列中. 在你的perl腳本中通常是這樣調用的:

  此例中,陣列名是in, 可以随便取名的. 在表單輸入解碼後, 你能讀取和處理這個name/value,方法是象下面這樣:

  這個将顯示名字name是thename的值value.

  如果你有多個用同樣名字的name對, cgi-lib.pl用(\0)分隔多個名字. 這樣可以正常處理你的腳本.

  基于表單的檔案上傳需要不同的表單輸入,有一些程式可以對其進行解碼。cgi-lib.pl 後來版本可以很好支援這個功能.

  找專門書籍學習吧: ftp://ds.internic.net/rfc/rfc1867.txt.

  按照本書闡述,大多數情況可以正常操作,在一些情況下不是這樣的,你可以翻閱說明書了解。

  為了在cgi中完成讨論組, 我們看看叫<isindex>的搜尋. 這是早期在浏覽器中用來向伺服器發出搜尋關鍵字的辦法,參看以前的資料。

  cgi腳本, 有時叫伺服器端腳本或網關腳本。 在internet上有很多免費資源,你可以搜尋下載下傳讀懂他們,當然都是英文的,如果你下決心翻譯他們(可能更加強了解). 這樣一舉兩得啊.

  注意:上述程式可以用ultra edit來編輯,注意轉換unix格式 ,必須采用unix格式存盤,再上載,用telnet登陸,在指令行鍵入perl sample.pl,看有無bug,再 在浏覽器中調用。cgi程式包括放置cgi的目錄一定要改屬性為777, 要寫入的html檔案也要改屬性為777.

cgi