概要
相关模块 | 相关指令 |
---|---|
CGI(公共网关接口[Common Gateway Interface])定义了网站服务器与外部内容协商程序之间交互的方法,通常是指CGI程序或者CGI脚本,是在网站上实现动态页面的最简单而常用的方法。本文将对如何在Apache网站服务器上建立CGI以及如何编写CGI程序作介绍。
配置Apache以允许CGI
要让CGI程序能正常运作,必须配置Apache以允许CGI的执行,其方法有多种。
ScriptAlias
ScriptAlias
指令使Apache允许执行一个特定目录中的CGI程序。当客户端请求此特定目录中的资源时,Apache假定其中文件都是CGI程序并试图运行。
ScriptAlias
指令形如:
ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
如果Apache被安装到默认的位置,默认的配置文件httpd.conf
中则会有上述配置。ScriptAlias
指令定义了映射到一个特定目录的URL前缀,与Alias
指令非常相似,两者一般都用于指定位于DocumentRoot
目录以外的目录,其区别是ScriptAlias
又多了一层含义,即其URL前缀中任何文件都被视为CGI程序。所以,上述例子会指示Apache,/cgi-bin/
应该指向/usr/local/apache/cgi-bin/
目录,且视之为CGI程序。
举例,如果有URL为http://www.example.com/cgi-bin/test.pl
的请求,Apache会试图执行/usr/local/apache/cgi-bin/test.pl
文件并返回其输出。当然,这个文件必须存在而且可执行,并以特定的方法产生输出,否则Apache返回一个出错消息。
ScriptAlias目录以外的CGI
由于安全原因,CGI程序通常被限制在ScriptAlias
指定的目录中,如此,管理员就可以严格地控制谁可以使用CGI程序。但是,如果采取了恰当的安全方法措施,则没有理由不允许其他目录中的CGI程序运行。比如,你可能希望用户在UserDir
指定的宿主目录中存放页面,而他们有自己的CGI程序,但无权存取cgi-bin
目录,这样,就产生了运行其他目录中CGI程序的需求。
用Options显式地允许CGI的执行
可以在主服务器配置文件中,使用Options
指令显式地允许特定目录中CGI的执行:
<Directory /usr/local/apache/htdocs/somedir>
Options +ExecCGI
</Directory>
上述指令使Apache允许CGI文件的执行。另外,还必须告诉服务器哪些文件是CGI文件。下面的AddHandler
指令告诉服务器所有带有cgi
或pl
后缀的文件是CGI程序:
AddHandler cgi-script cgi pl
.htaccess文件
.htaccess
文件是针对目录进行配置的一种方法。Apache在提供一个资源时,会在此资源所在目录中寻找.htaccess
文件,如果有,则使其中的指令生效。AllowOverride
指令决定了.htaccess
文件是否有效,它指定了哪些指令可以出现在其中,或者根本不允许使用。为此,需要在主服务器配置中如此配置:
AllowOverride Options
在.htaccess
文件中,需要如此配置:
Options +ExecCGI
以使Apache允许此目录中CGI程序的执行。
编写CGI程序
编写CGI程序和``常规''程序之间有两个主要的不同。
首先,在CGI程序的所有输出前面必须有一个MIME类型的头,即HTTP头,对浏览器指明所接收内容的类型,大多数情况下,形如:
Content-type: text/html
其次,输出要求是HTML形式的,或者是浏览器可以显示的其他某种形式。多数情况下,输出是HTML形式的,但偶然也会编写CGI程序以输出一个gif图片或者其他非HTML的内容。
除了这两点,编写CGI程序和编写其他程序大致相同。
第一个CGI程序
这个CGI程序例子在浏览器中打印一行文字。把下列存为first.pl
文件,并放在你的cgi-bin
目录中。
#!/usr/bin/perl
print "Content-type: text/html
";
print "Hello, World.";
即使不熟悉Perl语言,你也应该能看出它干了什么。第一行,告诉Apache这个文件可以用/usr/bin/perl
(或者任何你正在使用的shell)解释并执行。第二行,打印上述要求的内容类型说明,并带有两个换行,在头后面留出空行,以示HTTP头的结束。第三行,打印文字``Hello, World.''。程序到此结束。
打开你喜欢的浏览器并输入地址:
http://www.example.com/cgi-bin/first.pl
或者是你存放程序的其他位置,就可以在浏览器窗口中看到一行Hello, World.
。虽然并不怎么激动人心,但是一旦这个程序能正常运作,那么就可能运作其他任何程序。
程序还是不能运行!
从网络访问CGI程序,浏览器中可能会发生四种情况:
- CGI程序的输出
- 太好了!这说明一切正常。
- CGI程序的源代码或者一个"POST Method Not Allowed"消息
- 这说明Apache没有被正确配置以执行CGI程序,重新阅读configuring Apache看看遗漏了什么。
- 一个以"Forbidden"开头的消息
- 这说明有权限问题。参考Apache error log和下面的文件的权限。
- 一个"Internal Server Error"消息
- 查阅Apache error log,可以找到CGI程序产生的出错消息"Premature end of script headers"。对此,需要检查下列各项,以找出不能产生正确HTTP头的原因。
文件的权限
记住,服务器不是以你的用户身份运行的,就是说,在服务器启动后,拥有的是一个非特权用户的权限-通常是``nobody''或者``www'' -而需要更大的权限以允许文件的执行。通常,给予``nobody''足够的权限以执行文件的方法是,对文件赋予everyone execute权限:
chmod a+x first.pl
另外,如果需要对其他文件进行读取或写入,也必须对这些文件赋予正确的权限。
如果服务器被配置为使用suexec则是一个例外。这个程序允许CGI程序根据其所在虚拟主机或用户宿主目录的不同而以不同的用户权限运行。Suexec有极其严格的权限校验,任何校验失败都会使CGI程序运行失败而产生"Internal Server Error"。对此,需要检查suexec的日志文件以发现哪个安全校验出问题了。
路径信息
当你在命令行执行一个程序,某些信息会自动传给shell而无须你操心,比如一个路径,告诉shell你所引用的文件可以在哪儿找到。
但是,在CGI程序通过网站服务器执行时,则没有此路径,所以,你在CGI程序中引用的任何程序(如sendmail)都必须指定其完整的路径,使shell能找到它们以执行你的CGI程序。
一种普通的用法是,在CGI程序的第一行中指明解释器(通常是perl
),形如:
#!/usr/bin/perl
必须保证它的确指向解释器。
语法错误
多数CGI程序失败的原因在于程序本身有问题,尤其是在已经消除上述两种错误而CGI挂起的情况下。在用浏览器测试以前,先在命令行中执行你的程序,能够发现大多数的问题。
出错记录
出错记录是你的朋友。任何错误都会在出错记录中有记载,所以你应该首先查看它。如果你的网站空间提供者不允许访问出错记录,那么你应该考虑换一个空间提供者。学会阅读出错记录,可以快速找出问题并快速解决。
幕后是怎样操作的?
当你的CGI编程逐渐深入,理解幕后的操作,尤其是浏览器和服务器如何与其他的通讯,就变得有用了。虽然成功地写了一个程序打印``Hello, World.'',但并没有实际的用处。
环境变量
环境变量是使用计算机时到处都会用到的变量,比如路径(对实际文件的一个搜索路径以补全你的输入)、你的用户名以及你的终端类型等等。在命令行输入env
,可以得到你的标准的当天的环境变量列表。
在CGI处理过程中,服务器和浏览器都会设置环境变量,比如浏览器类型(Netscape, IE, Lynx)、服务器类型(Apache, IIS, WebSite)以及将要执行的CGI程序名称等等。
所有这些变量对CGI程序员都有效,但只是客户端-服务器通讯的一半内容。完整的变量列表见http://hoohoo.ncsa.uiuc.edu/cgi/env.html
这个简单的CGI程序列出了环境中所有的环境变量,Apache发行版的cgi-bin
中还有两个类似的程序。注意,有些变量是必须的,有些则是可选的,所以你可能会看见一些官方列表中没有的变量。另外,Apache提供了多种不同方法以在默认提供的变量中增加你的专用环境变量。
#!/usr/bin/perl
print "Content-type: text/html
";
foreach $key (keys %ENV) {
print "$key --> $ENV{$key}<br>";
}
STDIN和STDOUT
服务器和客户端之间的其他通讯都通过标准输入设备(STDIN
)和标准输出设备(STDOUT
)完成。通常,STDIN
是指键盘或者一个程序所作用于的一个文件,STDOUT
指控制台或显示器。
当你POST
一个网络表格到一个CGI程序时,表格中的数据被捆扎为一个特殊形式通过STDIN
传送给CGI程序,这样,这个程序就可以处理这些数据,仿佛这些数据来自键盘或者一个文件。
这种``特殊形式''很简单,一个字段名称及其值,中间用等号(=)连接,多个这样的字段对用与符号(&)连接。非常规字符,如空格、与符号和等号,被转换为其等值的十六进制以免出问题。整个字串形如:
name=Rich%20Bowen&city=Lexington&state=KY&sidekick=Squirrel%20Monkey
有时,你会发现URL后面缀有这样的字串。这种形式会使服务器以这个字串的内容设置环境变量QUERY_STRING
,称为GET
请求。你的HTML表格在FORM
标记中设置METHOD
属性,以指定传送数据的行为使GET
或者是POST
。
接着,你的程序必须把这个字串分离以获得有用的信息。所幸,有库和模块可以帮助你处理这些数据,还有为你的CGI程序达成其他目的的处理器。
CGI模块/库
编写CGI程序时,你应该考虑使用代码库或模块来完成多数琐碎的工作,以减少错误并更快地开发。
如果用Perl语言编写CGI程序,可用的模块见CPAN,最常用的模块是CGI.pm。也可以考虑用CGI::Lite,它实现了一个在多数程序中所有必须的最小功能集
如果用C语言编写CGI程序,则有很多选择,其中之一是CGIC库,来自http://www.boutell.com/cgic/。
更多资料
网上有大量的CGI资源。可以在Usenet组comp.infosystems.www.authoring.cgi和别人讨论CGI相关问题。HTML Writers Guild的-servers邮件列表是一个优秀的问题解答资源。更多资源可以在http://www.hwg.org/lists/hwg-servers/找到。
另外,还可以阅读CGI规范,其中有CGI程序操作的所有细节,原始版本见NCSA,另有一个更新草案见Common Gateway Interface RFC project。
当你向一个邮件列表或者新闻组提交CGI相关问题时,你应该确保提供了足够的信息以更简单地发现并解决问题,诸如:发生了什么事、你希望得到什么结果、结果与你所期望的有什么出入、你运行的服务器、CGI程序是用什么语言编写的、如果可能就提供那个讨厌的代码。
注意,绝不要把CGI相关问题提交到Apache bug database,除非你坚信发现的是Apache源代码中的问题。