Perl网站开发模块-CGI.pm
2013-06-19 14:58:32 阿炯

本站赞助商链接,请多关照。 CGI.pm is a large and widely used Perl module for programming Common Gateway Interface (CGI) web applications, providing a consistent API for receiving user input and producing HTML or XHTML output.

CGI - Handle Common Gateway Interface requests and responses

这是一个传统的可用于开发可交互式网站的功能模块,它已经存活了十多个年头并仍有其活动的舞台。经过多年的开发和维护,它从Perl 5.4版本开始成为实事上标准核心模块,通常与mod_perl配合使用,当然对FastCGI也有很好的支持,在CPAN上有大量与之相配合使用的模块供选择。

CGI.pm is a stable, complete and mature solution for processing and preparing HTTP requests and responses. Major features including processing form submissions, file uploads, reading and writing cookies, query string generation and manipulation, and processing and preparing HTTP headers. Some HTML generation utilities are included as well.

cgi.pm是一个稳定的、完整和成熟的网站解决方案和HTTP请求和响应处理模块。主要功能包括处理表单提交、文件上传、Cookie读写、动态查询生成和处理、HTTP头自定义响应等。当然也可生成HTML页面内容等。

当然,它也有一些在现在看起来不那么'现代'的缺点,比如支持过时的html元素,生成HTML页面,修改元素属性等像前端该做的工作的功能(那时的前端功能确实不及今天这样丰富)等。但这并不影响它在其它方面的使用,像cgi get/post参数处理、session管理、http头定义,还可以引入第三方模块来扩充其功能,像使用memcached来缓存对象、dbi来实现对数据库的操纵、各类的模板技术来让它摆脱直接的html处理,引入像jquery之类的js框架来实现更好的前端处理,让它更专注于后端功能等。

相比于Mojolicious这些后起的编程框架(更多相关框架,请参考此处),CGI.pm显得很局促。但不是它就没有了用武之地,可以使用第三方的模块来进行功能扩充,与其它组件组合同样能发挥出其强大的功能,Ajax不就是这样吗。笔者认为:开源的意义在于整合,任何大而全的应用是不足取的。


通用网关接口 (CGI) 是一组标准,用于定义 Web 服务器和自定义脚本之间的信息交换方式的接口标准。Web 服务器要执行的所有 CGI 程序都保存在预先配置的目录中。这个目录被称为 CGI 目录,按照惯例,它被命名为 /cgi-bin;Perl CGI 文件的扩展名为.cgi。《CGI通用网关与Nginx》一文中对CGI的工作原理有一定的介绍,非常值得一看。



HTTP 标头

Content-type: String
定义返回内容格式的 MIME 字符串。 示例是 Content-type:text/html

Expires: Date String
信息失效的日期。 浏览器应该使用它来决定何时需要刷新页面。 有效日期字符串的格式应为 1998 年 1 月 1 日 12:00:00 GMT。

Location: URL String
应返回的 URL,而不是请求的 URL。 您可以使用此字段将请求重定向到任何其他位置。

Last-modified: String
上次修改文件的日期。

Content-length: String
返回的数据的长度(以字节为单位)。 浏览器使用此值报告文件的估计下载时间。

Set-Cookie: String
设置通过 string 传递的 cookie


CGI 环境变量

所有 CGI 程序都可以访问以下环境变量。 这些变量在编写任何 CGI 程序时都起着重要作用。

CONTENT_TYPE
内容的数据类型。 当客户端向服务器发送附加内容时使用。 例如文件上传等。

CONTENT_LENGTH
查询信息的长度。 它仅适用于 POST 请求

HTTP_COOKIE
以 key & value 对的形式返回设置的 cookie。

HTTP_USER_AGENT
User-Agent request-header 字段包含有关发起请求的用户代理的信息。 它的网络浏览器名称。

PATH_INFO
CGI 脚本的路径。

QUERY_STRING
使用 GET 方法请求发送的 URL 编码信息。

REMOTE_ADDR
发出请求的远程主机的 IP 地址。 这对于日志记录或身份验证很有用。

REMOTE_HOST
发出请求的主机的完全限定名称。 如果此信息不可用,则可以使用 REMOTE_ADDR 获取 IR 地址。

REQUEST_METHOD
用于发出请求的方法。 最常用的方法是 GET 和 POST。

SCRIPT_FILENAME
CGI 脚本的完整路径。

SCRIPT_NAME
CGI 脚本的名称。

SERVER_NAME
服务器的主机名或 IP 地址。

SERVER_SOFTWARE
服务器正在运行的软件的名称和版本。


使用示例

列出 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:box 中。如果有密码或其他敏感信息要传递给服务器,则永远不应使用 GET 方法。 GET 方法有大小限制:请求字符串中只能传递 1024 个字符。

此信息使用 QUERY_STRING 标头传递,并且可以通过 QUERY_STRING 环境变量在的 CGI 程序中访问,可以在 CGI 程序中解析和使用该环境变量。可以通过简单地连接键和值对以及任何 URL 来传递信息,也可以使用 HTML <FORM> 标签使用 GET 方法传递信息。

简单 URL 示例:Get 方法

这是一个简单的 URL,它将使用 GET 方法将两个值传递给 hello_get.cgi 程序。
/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;


简单的 FORM 示例:GET 方法

这是一个简单的例子,它使用 HTML FORM 和提交按钮传递两个值。 我们将使用相同的 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 FORM 和提交按钮传递两个值。将使用 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>

这是上述表单编码的实际输出,输入名字和姓氏,然后单击提交按钮以查看结果。

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


下面是 radiobutton.cgi 脚本,用于处理网络浏览器为单选按钮提供的输入。

#!/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 框的表单的示例:

<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 脚本。

#!/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 程序

当有许多可用选项但只会选择一两个时,使用下拉框。这是带有一个下拉框的表单的示例:

<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 脚本。

#!/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 以键值对的形式设置和回顾。

设置 Cookies

将 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)是可选的。需要注意的是cookie 是在发送行 "Content-type:text/html\r\n\r\n 之前设置的。

检索 Cookies

检索所有设置的 cookie 非常容易 Cookie 存储在 CGI 环境变量 HTTP_COOKIE 中,它们将具有以下形式:
key1 = value1;key2 = value2;key3 = value3....

Here is an example of how to retrieve cookies.

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


在 Internet 上找到许多内置模块,它们提供了在 CGI 程序中使用的直接功能。如Berkeley cgi-lib.pl等第三方的CGI功能模块。

参考来源:

Perl Cgi 主要函数简介

Perl CGI 简介及指令示范

Perl CGI 安全编程点滴



最新版本:3.6


项目主页:http://search.cpan.org/dist/CGI/