Perl 和 CGI 教程



什麼是 CGI?

通用閘道器介面(CGI)是一套標準,定義了 Web 伺服器和自定義指令碼之間如何交換資訊。

CGI 規範目前由 NCSA 維護,NCSA 將 CGI 定義如下:

通用閘道器介面(CGI)是外部閘道器程式與資訊伺服器(如 HTTP 伺服器)介面的標準。

當前版本為 CGI/1.1,CGI/1.2 正在開發中。

網頁瀏覽

為了理解 CGI 的概念,讓我們看看當我們點選超連結瀏覽特定網頁或 URL 時會發生什麼。

  • 您的瀏覽器聯絡 HTTP Web 伺服器並請求 URL,即檔名。

  • Web 伺服器將解析 URL 並查詢該檔案,如果找到則傳送回瀏覽器,否則傳送錯誤訊息,指示您請求的檔案錯誤。

  • Web 瀏覽器接收 Web 伺服器的響應,並顯示接收到的檔案或錯誤訊息。

但是,可以設定 HTTP 伺服器,以便每當請求某個目錄中的檔案時,該檔案不會被髮送回;相反,它將作為程式執行,並且該程式輸出的任何內容都將傳送回以供您的瀏覽器顯示。此功能稱為通用閘道器介面或 CGI,程式稱為 CGI 指令碼。這些 CGI 程式可以是 PERL 指令碼、Shell 指令碼、C 或 C++ 程式等。

CGI 架構圖

CGI Architecture

Web 伺服器支援和配置

在繼續進行 CGI 程式設計之前,請確保您的 Web 伺服器支援 CGI 並且已配置為處理 CGI 程式。所有由 HTTP 伺服器執行的 CGI 程式都儲存在預配置的目錄中。此目錄稱為 CGI 目錄,按照慣例,其名稱為 /cgi-bin。按照慣例,PERL CGI 檔案的副檔名為 .cgi

第一個 CGI 程式

#!/usr/bin/perl

print "Content-type:text/html\r\n\r\n";
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>';

1;

輸出

Hello Word! This is my first CGI program

HTTP 頭

Content-type:text/html\r\n\r\n 行是 HTTP 頭的一部分,傳送到瀏覽器以理解內容。所有 HTTP 頭都將採用以下格式

HTTP Field Name: Field Content

例如

Content-typetext/html\r\n\r\n

在您的 CGI 程式設計中,您經常會使用一些其他重要的 HTTP 頭。

序號 頭和描述
1

Content-type: 字串

定義返回檔案的格式的 MIME 字串。例如 Content-type:text/html

2

Expires: 日期字串

資訊失效的日期。瀏覽器應使用此資訊來確定何時需要重新整理頁面。有效的日期字串應採用 01 Jan 1998 12:00:00 GMT 的格式。

3

Location: URL 字串

應返回的 URL,而不是請求的 URL。您可以使用此欄位將請求重定向到任何檔案。

4

Last-modified: 字串

資源上次修改的日期。

5

Content-length: 字串

返回資料的長度(以位元組為單位)。瀏覽器使用此值報告檔案的估計下載時間。

6

Set-Cookie: 字串

設定透過字串傳遞的 Cookie

CGI 環境變數

所有 CGI 程式都可以訪問以下環境變數。這些變數在編寫任何 CGI 程式時都起著重要作用。

序號 變數名和描述
1

CONTENT_TYPE

內容的資料型別。當客戶端向伺服器傳送附加內容時使用。例如檔案上傳等。

2

CONTENT_LENGTH

查詢資訊的長度。僅在 POST 請求中可用。

3

HTTP_COOKIE

以鍵值對的形式返回設定的 Cookie。

4

HTTP_USER_AGENT

User-Agent 請求頭欄位包含有關發起請求的使用者代理的資訊。它是 Web 瀏覽器的名稱。

5

PATH_INFO

CGI 指令碼的路徑。

6

QUERY_STRING

使用 GET 方法請求傳送的 URL 編碼資訊。

7

REMOTE_ADDR

發出請求的遠端主機的 IP 地址。這對於記錄或身份驗證很有用。

8

REMOTE_HOST

發出請求的主機的完全限定域名。如果此資訊不可用,則可以使用 REMOTE_ADDR 獲取 IR 地址。

9

REQUEST_METHOD

用於發出請求的方法。最常用的方法是 GET 和 POST。

10

SCRIPT_FILENAME

CGI 指令碼的完整路徑。

11

SCRIPT_NAME

CGI 指令碼的名稱。

12

SERVER_NAME

伺服器的主機名或 IP 地址。

13

SERVER_SOFTWARE

伺服器正在執行的軟體的名稱和版本。

#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "<font size=+1>Environment</font>\n";

foreach (sort keys %ENV) {
   print "<b>$_</b>: $ENV{$_}<br>\n";
}

1;

輸出

Environment CONTEXT_DOCUMENT_ROOT: 
CONTEXT_PREFIX: 
DOCUMENT_ROOT: 
GATEWAY_INTERFACE: 
GEOIP_ADDR: 
GEOIP_CONTINENT_CODE: 
GEOIP_COUNTRY_CODE: 
GEOIP_COUNTRY_NAME: 
HTTP_ACCEPT: 
HTTP_ACCEPT_ENCODING: 
HTTP_ACCEPT_LANGUAGE: 
HTTP_COOKIE: 
HTTP_HOST: 
HTTP_UPGRADE_INSECURE_REQUESTS: 
HTTP_USER_AGENT: 
HTTP_VIA: 
HTTP_X_FORWARDED_FOR: 
HTTP_X_FORWARDED_PROTO: 
HTTP_X_HOST: 
PATH: 
QUERY_STRING: 
REMOTE_ADDR: 
REMOTE_PORT: 
REQUEST_METHOD: 
REQUEST_SCHEME: 
REQUEST_URI: 
SCRIPT_FILENAME: 
SCRIPT_NAME: 
SCRIPT_URI: 
SCRIPT_URL: 
SERVER_ADDR: 
SERVER_ADMIN: 
SERVER_NAME: 
SERVER_PORT: 
SERVER_PROTOCOL: 
SERVER_SIGNATURE: 
SERVER_SOFTWARE: 
UNIQUE_ID: 

如何顯示“檔案下載”對話方塊?

有時您希望提供一個選項,使用者單擊連結後會彈出一個“檔案下載”對話方塊,而不是顯示實際內容。這非常簡單,可以透過 HTTP 頭實現。

此 HTTP 頭將不同於上一節中提到的頭。

例如,如果要使FileName 檔案可從給定連結下載,則其語法如下。

#!/usr/bin/perl

# 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 hear.
open( FILE, "<FileName" );
while(read(FILE, $buffer, 100) ) {
   print("$buffer");
}

GET 和 POST 方法

您一定遇到過許多需要將一些資訊從瀏覽器傳遞到 Web 伺服器,最終傳遞到 CGI 程式的情況。瀏覽器最常使用兩種方法將此資訊傳遞到 Web 伺服器。這些方法是 GET 方法和 POST 方法。

使用 GET 方法傳遞資訊

GET 方法傳送附加到頁面請求的編碼使用者資訊。頁面和編碼資訊由 ? 字元分隔,如下所示:http://www.test.com/cgi-bin/hello.cgi?key1=value1&key2=value2

GET 方法是將資訊從瀏覽器傳遞到 Web 伺服器的預設方法,它會生成一個長字串,顯示在瀏覽器的 Location: 框中。如果要將密碼或其他敏感資訊傳遞到伺服器,切勿使用 GET 方法。GET 方法有大小限制:請求字串中只能有 1024 個字元。

此資訊使用 QUERY_STRING 頭傳遞,並且可以透過 QUERY_STRING 環境變數在 CGI 程式中訪問。

您可以透過簡單地連線鍵值對以及任何 URL 來傳遞資訊,或者可以使用 HTML <FORM> 標記使用 GET 方法傳遞資訊。

簡單 URL 示例:GET 方法

這是一個簡單的 URL,它將使用 GET 方法將兩個值傳遞給 hello_get.cgi 程式。

以下是 hello_get.cgi 指令碼,用於處理 Web 瀏覽器提供的輸入。

#!/usr/bin/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;

if ($ENV{'REQUEST_METHOD'} eq "GET") {
   $buffer = $ENV{'QUERY_STRING'};
}

# Split information into name/value pairs
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

$first_name = $FORM{first_name};
$last_name  = $FORM{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 $first_name $last_name - Second CGI Program</h2>";
print "</body>";
print "</html>";

1;

輸出

Hello ZARA ALI .....

簡單表單示例:GET 方法

這是一個簡單的示例,它使用 HTML 表單和提交按鈕傳遞兩個值。我們將使用相同的 CGI 指令碼 hello_get.cgi 來處理此輸入。

<FORM action = "/cgi-bin/hello_get.cgi" 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 指令碼。

以下是 hello_post.cgi 指令碼,用於處理 Web 瀏覽器提供的輸入。此指令碼將處理 GET 和 POST 方法。

#!/usr/bin/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;

if ($ENV{'REQUEST_METHOD'} eq "POST") {
   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
   $buffer = $ENV{'QUERY_STRING'};
}

# Split information into name/value pairs
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

$first_name = $FORM{first_name};
$last_name  = $FORM{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 $first_name $last_name - Second CGI Program</h2>";
print "</body>";
print "</html>";

1;

讓我們再次使用與上面相同的示例,該示例使用 HTML 表單和提交按鈕傳遞兩個值。我們將使用 CGI 指令碼 hello_post.cgi 來處理此輸入。

<FORM action = "/cgi-bin/hello_post.cgi" 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/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;

if ($ENV{'REQUEST_METHOD'} eq "POST") {
   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
   $buffer = $ENV{'QUERY_STRING'};
}

# Split information into name/value pairs
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

if( $FORM{maths} ) {
   $maths_flag ="ON";
} else {
   $maths_flag ="OFF";
}

if( $FORM{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 : $maths_flag</h2>";
print "<h2> CheckBox Physics is : $physics_flag</h2>";
print "</body>";
print "</html>";

1;

將單選按鈕資料傳遞到 CGI 程式

當只需要選擇一個選項時,使用單選按鈕。

以下是具有兩個單選按鈕的表單的 HTML 程式碼示例:

<form action = "/cgi-bin/radiobutton.cgi" 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.cgi 指令碼,用於處理 Web 瀏覽器為單選按鈕提供的輸入。

#!/usr/bin/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;

if ($ENV{'REQUEST_METHOD'} eq "POST") {
   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
   $buffer = $ENV{'QUERY_STRING'};
}

# Split information into name/value pairs
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

$subject = $FORM{subject};

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 $subject</h2>";
print "</body>";
print "</html>";

1;

將文字區域資料傳遞到 CGI 程式

當需要將多行文字傳遞到 CGI 程式時,使用 TEXTAREA 元素。

以下是具有 TEXTAREA 框的表單的 HTML 程式碼示例:

<form action = "/cgi-bin/textarea.cgi" method = "POST" target = "_blank">
   <textarea name = "textcontent" cols = 40 rows = 4>
      Type your text here...
   </textarea>
   <input type = "submit" value = "Submit">
</form>

此程式碼的結果是以下表單:

以下是 textarea.cgi 指令碼,用於處理 Web 瀏覽器提供的輸入。

#!/usr/bin/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;

if ($ENV{'REQUEST_METHOD'} eq "POST") {
   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
   $buffer = $ENV{'QUERY_STRING'};
}

# Split information into name/value pairs
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

$text_content = $FORM{textcontent};

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 $text_content</h2>";
print "</body>";
print "</html>";

1;

將下拉框資料傳遞到 CGI 程式

當有很多選項可用但僅選擇一個或兩個時,使用下拉框。

以下是具有一個下拉框的表單的 HTML 程式碼示例

<form action = "/cgi-bin/dropdown.cgi" 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>

此程式碼的結果是以下表單:

以下是 dropdown.cgi 指令碼,用於處理 Web 瀏覽器提供的輸入。

#!/usr/bin/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;

if ($ENV{'REQUEST_METHOD'} eq "POST") {
   read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
   $buffer = $ENV{'QUERY_STRING'};
}

# Split information into name/value pairs
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

$subject = $FORM{dropdown};

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 $subject</h2>";
print "</body>";
print "</html>";

1;

在 CGI 中使用 Cookie

HTTP 協議是無狀態協議。但是對於商業網站,需要在不同頁面之間維護會話資訊。例如,一個使用者註冊在完成許多頁面後結束。但是如何在所有網頁中維護使用者的會話資訊。

在許多情況下,使用 Cookie 是記住和跟蹤偏好、購買、佣金和其他資訊以獲得更好的訪客體驗或網站統計的最有效方法。

工作原理

您的伺服器以 Cookie 的形式向訪問者的瀏覽器傳送一些資料。瀏覽器可能會接受此 Cookie。如果接受,它將作為純文字記錄儲存在訪問者的硬碟驅動器上。現在,當訪問者到達您網站上的另一個頁面時,Cookie 可供檢索。檢索後,您的伺服器就會知道/記住儲存的內容。

Cookie 是一個包含 5 個可變長度欄位的純文字資料記錄 -

  • Expires - Cookie 將過期的日期。如果為空,則 Cookie 將在訪問者退出瀏覽器時過期。

  • Domain - 您網站的域名。

  • Path - 設定 Cookie 的目錄或網頁的路徑。如果您想從任何目錄或頁面檢索 Cookie,則此欄位可以為空。

  • Secure - 如果此欄位包含“secure”一詞,則 Cookie 只能使用安全伺服器檢索。如果此欄位為空,則不存在此類限制。

  • Name=Value - Cookie 以鍵值對的形式設定和檢索。

設定 Cookie

將 Cookie 傳送到瀏覽器非常簡單。這些 Cookie 將與 HTTP 標頭一起傳送。假設您想將 UserID 和 Password 作為 Cookie 設定。因此,操作如下 -

#!/usr/bin/perl

print "Set-Cookie:UserID=XYZ;\n";
print "Set-Cookie:Password=XYZ123;\n";
print "Set-Cookie:Expires=Tuesday, 31-Dec-2007 23:12:40 GMT";\n";
print "Set-Cookie:Domain=www.tutorialspoint.com;\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/perl
$rcvd_cookies = $ENV{'HTTP_COOKIE'};
@cookies = split /;/, $rcvd_cookies;

foreach $cookie ( @cookies ) {
   ($key, $val) = split(/=/, $cookie); # splits on the first =.
   $key =~ s/^\s+//;
   $val =~ s/^\s+//;
   $key =~ s/\s+$//;
   $val =~ s/\s+$//;
	
   if( $key eq "UserID" ) {
      $user_id = $val;
   } elsif($key eq "Password") {
      $password = $val;
   }
}

print "User ID  = $user_id\n";
print "Password = $password\n";

This will produce following result
User ID = XYZ
Password = XYZ123

CGI 模組和庫

您將在網際網路上找到許多內建模組,這些模組為您提供直接函式以在您的 CGI 程式中使用。以下是重要的模組。

廣告