<-
Apache > HTTP 服务器 > 文档 > 版本 2.4 > 操作指南 / 教程

Apache 教程:使用 CGI 生成动态内容

可用语言:  en  |  es  |  fr  |  ja  |  ko 

Support Apache!

另请参阅

top

简介

CGI(通用网关接口)定义了一种 Web 服务器与外部内容生成程序交互的方式,这些程序通常被称为 CGI 程序或 CGI 脚本。它是一种在您的网站上放置动态内容的简单方法,可以使用您最熟悉的任何编程语言。本文档将介绍如何在您的 Apache Web 服务器上设置 CGI 以及开始编写 CGI 程序。

top

配置 Apache 以允许 CGI

为了使您的 CGI 程序正常工作,您需要配置 Apache 以允许 CGI 执行。有几种方法可以做到这一点。

注意:如果 Apache 是使用共享模块支持构建的,您需要确保模块已加载;在您的 httpd.conf 中,您需要确保 LoadModule 指令没有被注释掉。正确配置的指令可能如下所示
LoadModule cgid_module modules/mod_cgid.so
在 Windows 上,或使用非线程化的 MPM(如 prefork),正确配置的指令可能如下所示
LoadModule cgi_module modules/mod_cgi.so

ScriptAlias

ScriptAlias 指令告诉 Apache 特定目录是为 CGI 程序预留的。Apache 将假定此目录中的每个文件都是 CGI 程序,并在客户端请求该特定资源时尝试执行它。

ScriptAlias 指令如下所示

ScriptAlias "/cgi-bin/" "/usr/local/apache2/cgi-bin/"

显示的示例来自您的默认 httpd.conf 配置文件,如果您在默认位置安装了 Apache。 ScriptAlias 指令与 Alias 指令非常相似,后者定义一个要映射到特定目录的 URL 前缀。 AliasScriptAlias 通常用于位于 DocumentRoot 目录之外的目录。 AliasScriptAlias 之间的区别在于 ScriptAlias 具有额外的含义,即该 URL 前缀下的所有内容都将被视为 CGI 程序。因此,上面的示例告诉 Apache 任何以 /cgi-bin/ 开头的资源请求都应从 /usr/local/apache2/cgi-bin/ 目录提供服务,并且应被视为 CGI 程序。

例如,如果请求 URL http://www.example.com/cgi-bin/test.pl,Apache 将尝试执行文件 /usr/local/apache2/cgi-bin/test.pl 并返回输出。当然,该文件必须存在,并且是可执行的,并且以特定方式返回输出,否则 Apache 将返回错误消息。

ScriptAlias 目录之外的 CGI

出于安全原因,CGI 程序通常被限制在 ScriptAlias 的目录中。通过这种方式,管理员可以严格控制谁被允许使用 CGI 程序。但是,如果采取了适当的安全预防措施,则没有理由不能从任意目录运行 CGI 程序。例如,您可能希望让用户使用 UserDir 指令在其主目录中拥有 Web 内容。如果他们想拥有自己的 CGI 程序,但没有访问主 cgi-bin 目录的权限,他们将需要能够在其他地方运行 CGI 程序。

在任意目录中允许 CGI 执行有两个步骤。首先,必须使用 AddHandlerSetHandler 指令激活 cgi-script 处理程序。其次,必须在 Options 指令中指定 ExecCGI

显式使用 Options 允许 CGI 执行

您可以在主服务器配置文件中显式使用 Options 指令,以指定在特定目录中允许 CGI 执行

<Directory "/usr/local/apache2/htdocs/somedir">
    Options +ExecCGI
</Directory>

上面的指令告诉 Apache 允许执行 CGI 文件。您还需要告诉服务器哪些文件是 CGI 文件。以下 AddHandler 指令告诉服务器将所有扩展名为 cgipl 的文件视为 CGI 程序

AddHandler cgi-script .cgi .pl

.htaccess 文件

.htaccess 教程 展示了如果您没有访问 httpd.conf 的权限,如何激活 CGI 程序。

用户目录

要允许用户目录中任何以 .cgi 结尾的文件执行 CGI 程序,您可以使用以下配置。

<Directory "/home/*/public_html">
    Options +ExecCGI
    AddHandler cgi-script .cgi
</Directory>

如果您希望指定用户目录的 cgi-bin 子目录,其中所有内容都将被视为 CGI 程序,您可以使用以下内容。

<Directory "/home/*/public_html/cgi-bin">
    Options ExecCGI
    SetHandler cgi-script
</Directory>
top

编写 CGI 程序

``常规''编程和 CGI 编程之间有两个主要区别。

首先,CGI 程序的所有输出都必须以 MIME 类型 标头开头。这是一个 HTTP 标头,它告诉客户端它正在接收什么类型的內容。大多数情况下,它看起来像

Content-type: text/html

其次,您的输出需要以 HTML 或浏览器能够显示的其他格式呈现。大多数情况下,这将是 HTML,但有时您可能会编写一个输出 gif 图像或其他非 HTML 内容的 CGI 程序。

除了这两点之外,编写 CGI 程序看起来与您可能编写的任何其他程序非常相似。

您的第一个 CGI 程序

以下是一个示例 CGI 程序,它向您的浏览器打印一行。输入以下内容,将其保存到名为 first.pl 的文件中,并将其放在您的 cgi-bin 目录中。

#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello, World.";

即使您不熟悉 Perl,您也应该能够看到这里发生了什么。第一行告诉 Apache(或您碰巧运行的任何 shell)该程序可以通过将文件馈送到 /usr/bin/perl 位置找到的解释器来执行。第二行打印我们讨论过的内容类型声明,后面跟着两个回车换行对。这在标头之后放置了一条空行,以指示 HTTP 标头的结束和正文的开始。第三行打印字符串“Hello, World.”。就这样结束了。

如果您打开您喜欢的浏览器并告诉它获取地址

http://www.example.com/cgi-bin/first.pl

或者您放置文件的位置,您将在浏览器窗口中看到一行 Hello, World. 出现。它不是很有趣,但一旦您让它正常工作,您就有很大机会让几乎所有东西都正常工作。

top

但它仍然无法正常工作!

当您尝试从 Web 访问您的 CGI 程序时,您可能会在浏览器中看到四种基本情况

您的 CGI 程序的输出
太好了!这意味着一切都正常工作。如果输出正确,但浏览器没有正确处理它,请确保您在 CGI 程序中设置了正确的内容类型。
您的 CGI 程序的源代码或“POST 方法不允许”消息
这意味着您没有正确配置 Apache 以处理您的 CGI 程序。重新阅读有关 配置 Apache 的部分,并尝试找出您错过了什么。
以“禁止”开头的消息
这意味着存在权限问题。检查 Apache 错误日志 和下面有关 文件权限 的部分。
显示“内部服务器错误”的消息
如果您检查 Apache 错误日志,您可能会发现它显示“脚本标头过早结束”,可能还会伴随您的 CGI 程序生成的错误消息。在这种情况下,您将需要检查以下每个部分,以查看是什么可能阻止您的 CGI 程序发出正确的 HTTP 标头。

文件权限

请记住,服务器不是以您的身份运行的。也就是说,当服务器启动时,它以非特权用户的权限运行——通常是 nobodywww——因此它需要额外的权限才能执行您拥有的文件。通常,为文件提供足够的权限以便 nobody 可以执行它的方法是为每个人授予该文件的执行权限

chmod a+x first.pl

此外,如果您的程序从任何其他文件读取或写入任何其他文件,则这些文件需要具有正确的权限才能允许这样做。

路径信息和环境

当您从命令行运行程序时,您会有一些信息传递给 shell,而您无需考虑它。例如,您有一个 PATH,它告诉 shell 它可以在哪里查找您引用的文件。

当程序通过 Web 服务器作为 CGI 程序运行时,它可能没有相同的 PATH。您在 CGI 程序中调用的任何程序(例如 sendmail)都需要通过完整路径指定,以便 shell 在尝试执行您的 CGI 程序时能够找到它们。

这种情况的常见表现形式是 CGI 程序第一行中指示的脚本解释器(通常是 perl)的路径,它看起来像

#!/usr/bin/perl

确保这确实是解释器的路径。

在 Windows 上编辑 CGI 脚本时,可能会将行尾字符附加到解释器路径。确保文件以 ASCII 模式传输到服务器。如果这样做,由于无法识别的行尾字符被解释为解释器文件名的部分,可能会导致操作系统发出“找不到命令”警告。

缺少环境变量

如果您的 CGI 程序依赖于非标准 环境变量,您需要确保 Apache 传递了这些变量。

当您在环境中丢失 HTTP 标头时,请确保它们按照 RFC 2616 第 4.2 节的格式进行格式化:标头名称必须以字母开头,后面只能跟字母、数字或连字符。任何违反此规则的标头将被静默丢弃。

程序错误

大多数情况下,当 CGI 程序失败时,是因为程序本身存在问题。当您掌握了 CGI 的基本知识后,这种情况尤其如此,您不再犯上述两个错误。首先要做的是确保您的程序在通过 Web 服务器测试之前从命令行运行。例如,尝试

cd /usr/local/apache2/cgi-bin
./first.pl

(不要调用 perl 解释器。shell 和 Apache 应该使用脚本第一行上的 路径信息 找到解释器。)

您首先看到程序写入的内容应该是一组 HTTP 标头,包括 Content-Type,后面跟着一个空行。如果您看到其他任何内容,Apache 将在您尝试通过服务器运行它时返回 Premature end of script headers 错误。有关更多详细信息,请参阅上面的 编写 CGI 程序

错误日志

错误日志是您的朋友。任何出错都会在错误日志中生成消息。您应该始终首先查看那里。如果托管网站的位置不允许您访问错误日志,您可能应该将网站托管在其他地方。学会阅读错误日志,您会发现几乎所有问题都可以快速识别并快速解决。

Suexec

suexec 支持程序允许 CGI 程序在不同的用户权限下运行,具体取决于它们位于哪个虚拟主机或用户主目录中。Suexec 具有非常严格的权限检查,任何检查失败都会导致您的 CGI 程序以 Premature end of script headers 失败。

要检查您是否正在使用 suexec,请运行 apachectl -V 并检查 SUEXEC_BIN 的位置。如果 Apache 在启动时在那里找到一个 suexec 二进制文件,则 suexec 将被激活。

除非您完全了解 suexec,否则您不应该使用它。要禁用 suexec,只需删除(或重命名)由 SUEXEC_BIN 指向的 suexec 二进制文件,然后重新启动服务器。如果您在阅读了有关 suexec 的信息后,仍然希望使用它,那么运行 suexec -V 以找到 suexec 日志文件的位置,并使用该日志文件找到您违反的策略。

top

幕后发生了什么?

随着您在 CGI 编程方面变得更加熟练,了解幕后发生的事情将变得很有用。具体来说,浏览器和服务器如何相互通信。因为虽然编写一个打印“Hello, World.”的程序很好,但它并不特别有用。

环境变量

环境变量是在您使用计算机时围绕您的值。它们是有用的东西,例如您的路径(当您键入命令时,计算机在其中搜索实现该命令的实际文件)、您的用户名、您的终端类型等等。要查看您正常、日常环境变量的完整列表,请在命令提示符下键入 env

在 CGI 事务期间,服务器和浏览器也会设置环境变量,以便它们可以相互通信。这些东西包括浏览器类型(Netscape、IE、Lynx)、服务器类型(Apache、IIS、WebSite)、正在运行的 CGI 程序的名称等等。

这些变量可供 CGI 程序员使用,并且是客户端-服务器通信故事的一半。所需变量的完整列表位于 Common Gateway Interface RFC 中。

这个简单的 Perl CGI 程序将显示所有正在传递的环境变量。Apache 发行版的 cgi-bin 目录中包含两个类似的程序。请注意,某些变量是必需的,而另一些是可选的,因此您可能会看到一些不在官方列表中的变量。此外,Apache 提供了许多不同的方法供您 将您自己的环境变量 添加到默认提供的基本环境变量中。

#!/usr/bin/perl
use strict;
use warnings;

print "Content-type: text/html\n\n";
foreach my $key (keys %ENV) {
    print "$key --> $ENV{$key}<br>";
}

STDIN 和 STDOUT

服务器和客户端之间的其他通信通过标准输入 (STDIN) 和标准输出 (STDOUT) 进行。在正常的日常环境中,STDIN 表示键盘,或程序被赋予作用的文件,而 STDOUT 通常表示控制台或屏幕。

当您将 Web 表单 POST 到 CGI 程序时,该表单中的数据将捆绑成一种特殊格式,并通过 STDIN 传递到您的 CGI 程序。然后,程序可以像从键盘或文件输入一样处理这些数据

“特殊格式”非常简单。字段名称及其值用等号 (=) 连接在一起,值对用与号 (&) 连接在一起。空格、与号和等号等不方便的字符将转换为它们的十六进制等效项,以防止它们妨碍工作。整个数据字符串可能看起来像

name=Rich%20Bowen&city=Lexington&state=KY&sidekick=Squirrel%20Monkey

您有时也会看到这种类型的字符串附加到 URL。当这样做时,服务器将该字符串放入名为 QUERY_STRING 的环境变量中。这称为 GET 请求。您的 HTML 表单通过在 FORM 标签中设置 METHOD 属性来指定使用 GET 还是 POST 来传递数据。

然后,您的程序负责将该字符串拆分为有用的信息。幸运的是,有库和模块可用于帮助您处理这些数据,以及处理 CGI 程序的其他方面。

top

CGI 模块/库

当您编写 CGI 程序时,您应该考虑使用代码库或模块来为您完成大部分繁重的工作。这会导致更少的错误和更快的开发。

如果您正在 Perl 中编写 CGI 程序,则可以在 CPAN 上获得模块。最流行的模块是 CGI.pm。您也可以考虑 CGI::Lite,它实现了最小的功能集,这在大多数程序中都是您所需要的。

如果您正在 C 中编写 CGI 程序,则有多种选择。其中之一是来自 https://web.mit.edu/wwwdev/www/cgic.htmlCGIC 库。

top

有关更多信息

当前的 CGI 规范可在 Common Gateway Interface RFC 中找到。

当您发布有关遇到的 CGI 问题的疑问时,无论是发布到邮件列表还是新闻组,请确保提供有关发生的事情、您期望发生的事情以及实际发生的事情与预期不同的方式、您正在运行的服务器、您的 CGI 程序的语言以及(如果可能)导致问题的代码的足够信息。这将使查找问题变得更加简单。

请注意,有关 CGI 问题的疑问绝不应发布到 Apache 错误数据库,除非您确定在 Apache 源代码中找到了问题。

可用语言:  en  |  es  |  fr  |  ja  |  ko 

top

评论

注意
这不是问答部分。此处放置的评论应指向有关改进文档或服务器的建议,如果它们被实施或被认为无效/主题外,可能会被我们的版主删除。有关如何管理 Apache HTTP Server 的问题应发送到我们的 IRC 频道 #httpd(在 Libera.chat 上)或发送到我们的 邮件列表