Apache 教程:使用 CGI 的动态内容

Introduction

Related ModulesRelated Directives
mod_alias
mod_cgi
mod_cgid
AddHandler
Options
ScriptAlias

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

配置 Apache 以允许 CGI

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

Warning

注意:如果 Apache 是使用共享模块支持构建的,则需要确保已加载该模块;否则,请确保已加载。在您的httpd.conf中,您需要确保LoadModule指令未被 Comments 掉。正确配置的指令可能如下所示:

LoadModule cgid_module modules/mod_cgid.so

在 Windows 上,或使用非线程 MPM(如 prefork),正确配置的指令可能如下所示:

LoadModule cgi_module modules/mod_cgi.so

ScriptAlias

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

ScriptAlias指令如下所示:

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

如果将 Apache 安装在默认位置,则显示的示例来自默认的httpd.conf配置文件。 ScriptAlias指令与Alias指令非常相似,后者定义了要 Map 到特定目录的 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 将返回错误消息。

CGI 在 ScriptAlias 目录之外

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

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

明确使用选项来允许 CGI 执行

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

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

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

AddHandler cgi-script .cgi .pl

.htaccess files

.htaccess tutorial显示了您无权访问httpd.conf时如何激活 CGI 程序。

User Directories

要允许对用户目录中以.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>

编写 CGI 程序

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

首先,您 CGI 程序的所有输出必须以MIME-typeHeaders 开头。这是 HTTP Headers,告诉 Client 端它正在接收哪种内容。大多数时候,这看起来像:

Content-type: text/html

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

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

您的第一个 CGI 程序

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

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

即使您不熟悉 Perl,您也应该能够看到这里发生的情况。第一行告诉 Apache(或您碰巧在其下运行的任何 Shell)可以通过将文件馈送到位置/usr/bin/perl的解释器来执行该程序。第二行显示我们所讨论的 Content Type 声明,其后是两个回车换行符。这会在 Headers 之后放置一个空白行,以指示 HTTP Headers 的结尾和正文的开头。第三行显示字符串“ Hello,World”。到此为止。

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

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

或将文件放在任何位置,都会在浏览器窗口中看到一行Hello, World.。这不是很令人兴奋,但是一旦您能够正常工作,您就有很大的机会来使几乎所有工作正常进行。

但仍无法正常工作!

当您尝试从网络访问 CGI 程序时,可能会在浏览器中看到以下四个基本信息:

  • 您的 CGI 程序的输出

    • 大!这意味着一切正常。如果输出正确,但浏览器未正确处理,请确保在 CGI 程序中设置了正确的Content-Type
  • 您的 CGI 程序的源代码或“不允许使用 POST 方法”消息

    • 这意味着您没有正确配置 Apache 以处理您的 CGI 程序。重读configuring Apache上的部分,然后尝试查找您错过的内容。
  • 以“禁止”开头的消息

  • 一条消息,指出“内部服务器错误”

    • 如果您检查Apache 错误日志,则可能会发现它显示“脚本头过早结束”,以及 CGI 程序生成的错误消息。在这种情况下,您将需要检查以下每个部分,以查看可能阻止 CGI 程序发出正确的 HTTP Headers 的原因。

File permissions

请记住,服务器不会像您那样运行。也就是说,服务器启动时,将以无特权用户(通常为nobodywww)的权限运行,因此它将需要额外的权限才能执行您拥有的文件。通常,授予文件足够的权限以供nobody执行的方法是向所有人授予对该文件的执行权限:

chmod a+x first.pl

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

路径信息和环境

从命令行运行程序时,无需考虑即可将某些信息传递到 Shell。例如,您有一个PATH,它告诉 Shell 在哪里可以找到您引用的文件。

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

一个常见的体现是 CGI 程序第一行中指示的脚本解释器(通常为perl)的路径,如下所示:

#!/usr/bin/perl

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

Warning

在 Windows 上编辑 CGI 脚本时,行尾字符可能会附加到解释器路径中。确保随后将文件以 ASCII 模式传输到服务器。否则,由于无法识别的行尾字符将被解释为解释器文件名的一部分,因此可能会导致 os 发出“找不到命令”警告。

缺少环境变量

如果您的 CGI 程序依赖于非标准environment variables,则需要确保这些变量由 Apache 传递。

当您错过环境中的 HTTP Headers 时,请确保它们按照RFC 2616第 4.2 节的格式设置:Headers 名称必须以字母开头,然后只能是字母,数字或连字符。任何违反此规则的 Headers 都将被静默删除。

Program errors

大多数时候,CGI 程序失败是由于程序本身存在问题。一旦掌握了这些 CGI 知识,并且不再犯上述两个错误,就尤其如此。首先要做的是确保在通过 Web 服务器测试程序之前,从命令行运行程序。例如,尝试:

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

(不要调用perl解释器.shell 和 Apache 应该使用脚本第一行的path information查找解释器。)

您所看到的由程序编写的第一件事应该是一组 HTTP Headers,包括Content-Type,后跟空白行。如果看到其他任何内容,如果您尝试通过服务器运行它,Apache 将返回Premature end of script headers错误。有关更多详细信息,请参见上面的编写 CGI 程序

Error logs

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

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 日志文件的位置,然后使用该日志文件查找您违反的策略。

幕后发生了什么?

随着 CGI 编程的 Developing,您将对了解幕后发生的事情变得很有用。具体来说,浏览器和服务器之间如何通信。因为尽管编写一个打印“ Hello,World。”的程序非常好,但是它并不是特别有用。

Environment variables

环境变量是在您使用计算机时在您周围 Float 的值。它们很有用,例如您的路径(在计算机上键入命令时,计算机会在其中搜索实际文件以执行命令),用户名,终端类型等等。有关日常环境变量的完整列表,请在命令提示符下键入env

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

这些变量可供 CGI 程序员使用,并且只是 Client 端与服务器之间通讯的一半。所需变量的完整列表位于通用网关接口 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

服务器和 Client 端之间的其他通信通过标准 Importing(STDIN)和标准输出(STDOUT)进行。在正常的日常情况下,STDIN表示键盘或程序可以操作的文件,而STDOUT通常表示控制台或屏幕。

当您将POST Web 表单添加到 CGI 程序时,该表单中的数据将 Binding 成一种特殊格式,并通过STDIN传递给您的 CGI 程序。然后,程序可以像处理来自键盘或文件的数据一样处理该数据。

“特殊格式”非常简单。字段名称及其值用等号(=)连接在一起,而值对用&符(&)连接在一起。不方便的字符(例如空格,&符和等号)将转换为等效的十六进制,以免影响作品。整个数据字符串可能类似于:

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

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

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

CGI modules/libraries

编写 CGI 程序时,应考虑使用代码库或模块来完成大部分繁琐的工作。这样可以减少错误并加快开发速度。

如果您在 Perl 中编写 CGI 程序,则模块CPAN上可用。为此,最受欢迎的模块是CGI.pm。您可能还考虑CGI::Lite,它实现了最少的功能集,这是大多数程序中所需要的。

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

有关更多信息

当前的 CGI 规范位于通用网关接口 RFC中。

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

请注意,除非您确定在 Apache 源代码中发现问题,否则有关 CGI 问题的问题应该“永远不要”发布到 Apache 错误数据库。