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 瀏覽器接收來自 Web 伺服器的響應,並在找不到檔案的情況下顯示接收到的檔案內容或錯誤訊息。

但是,可以這樣設定 HTTP 伺服器:每當請求某個目錄中的檔案時,該檔案不會被髮送回來;相反,它作為程式執行,並且該程式輸出的任何結果都會被髮送回瀏覽器以供顯示。這可以透過使用 Web 伺服器中提供的特殊功能來完成,該功能稱為公共閘道器介面或 CGI,並且伺服器執行以產生最終結果的此類程式稱為 CGI 指令碼。這些 CGI 程式可以是 PERL 指令碼、Shell 指令碼、C 或 C++ 程式等。

CGI 架構圖

CGI Architecture

Web 伺服器支援和配置

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

第一個 CGI 程式

這是一個簡單的連結,它連結到名為hello.cgi的 CGI 指令碼。此檔案已儲存在/cgi-bin/目錄中,其內容如下。在執行 CGI 程式之前,請確保使用chmod 755 hello.cgi UNIX 命令更改檔案的模式。

#!/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.cgi連結,則請求將傳送到 Web 伺服器,Web 伺服器將在 /cgi-bin 目錄中搜索 hello.cgi,執行它,並將生成的任何結果傳送回 Web 瀏覽器,結果如下:

Hello Word! This is my first CGI program

此 hello.cgi 指令碼是一個簡單的 Perl 指令碼,它將其輸出寫入 STDOUT 檔案(即螢幕)。有一個重要的額外功能,即要列印的第一行Content-type:text/html\r\n\r\n。此行傳送回瀏覽器並指定要在瀏覽器螢幕上顯示的內容型別。現在您必須瞭解 CGI 的基本概念,您可以使用 Perl 編寫許多複雜的 CGI 程式。此指令碼還可以與任何其他外部系統互動以交換資訊,例如資料庫、Web 服務或任何其他複雜的介面。

理解 HTTP 頭

第一行Content-type:text/html\r\n\r\n是 HTTP 頭的一部分,傳送到瀏覽器以便瀏覽器能夠理解來自伺服器端的內容。所有 HTTP 頭都將採用以下形式:

HTTP Field Name: Field Content

例如:

Content-type:text/html\r\n\r\n

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

序號 頭 & 描述
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

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

這是一個小的 CGI 程式,用於列出 Web 伺服器支援的所有 CGI 變數。單擊此連結以檢視結果 獲取環境變數

#!/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;

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

有時您希望提供一個選項,使用者單擊連結時會向用戶彈出一個“檔案下載”對話方塊,而不是顯示實際內容。這非常容易實現,可以透過 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 方法將編碼的使用者資訊附加到頁面 URL 本身。頁面和編碼資訊由 ? 字元分隔,如下所示:

http://www.test.com/cgi-bin/hello.cgi?key1=value1&key2=value2

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

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

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

簡單的 URL 示例:GET 方法

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

https://tutorialspoint.tw/cgi-bin/hello_get.cgi?first_name=ZARA&last_name=ALI

以下是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;

簡單的表單示例: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>

以下是上述表單程式碼的實際輸出。現在您可以輸入名字和姓氏,然後單擊提交按鈕以檢視結果。

名字

姓氏

使用 POST 方法傳遞資訊

將資訊傳遞給CGI程式的一種更可靠的方法是POST方法。此方法打包資訊的方式與GET方法完全相同,但它不是在URL中的?之後以文字字串的形式傳送資訊,而是將其作為HTTP標頭的一部分,以單獨訊息的形式傳送。Web伺服器以標準輸入的形式將此訊息提供給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>

以下是上述表單程式碼的實際輸出,您輸入名和姓,然後單擊提交按鈕檢視結果。

名字

姓氏

將複選框資料傳遞給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/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>

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

數學物理

以下是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元素。這是一個帶有文字區域框的表單的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 goes here....

在這裡,我們使用Set-Cookie HTTP標頭來設定Cookie。設定Cookie屬性(如Expires、Domain和Path)是可選的。重要的是要注意,在傳送魔術行"Content-type:text/html\r\n\r\n"之前設定Cookie。

檢索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";

這將產生以下結果,前提是在呼叫檢索Cookie指令碼之前已設定上述Cookie。

User ID = XYZ
Password = XYZ123

CGI模組和庫

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

廣告