CGI即通用网关接口(Common Gateway Interface),1993年由美国NCSA(National Center for Supercomputing Applications)发明。它具有简单易用、语言无关的特点。虽然今天已经少有人直接使用CGI进行编程,但它仍被主流的Web服务器,如Apache、IIS、Nginx1等,所广泛支持。另外,它还影响了其他一些服务器端技术,如PHP、Python WSGI、Ruby Rack等——在这些技术里你都能看到CGI的影子。所以它仍然值得学习。
先来看一个最简单的CGI程序:
#!/usr/bin/perl print "Content-type: text/plain\n", "\n"; print "Hello, CGI!";
把这个程序保存到文件hello.pl,并假设它对应的URL是http://localhost/cgi-bin/hello.pl。在浏览器中访问这个URL,你就能看到程序的输出结果——一个普通文本:
Hello, CGI!
在前面HTTP - 服务器应答一节我们已经了解了HTTP应答的格式,对照一下,你会发现这个CGI程序的输出正是如此:首先是若干头部(header),一个一行(这里仅有一个Content-Type头);接着是一个空行(注意Content-Type这一行结尾输出了两个”\n”:第一个是头部的换行,第二个是分隔头部和正文的空行);最后是消息正文。与之前不同的是,这里我们没有输出应答状态行(Status Line)——这有一些特别:如果CGI程序没有指明,缺省的状态代码是200;如果想返回其他代码,需要用到一个特殊的Status头部(header),如:
print "Status: 404 Not found\n";
CGI程序可以动态产生任何内容,只要按照HTTP应答的格式向标准输出(stdout)设备输出这些内容即可。另外,CGI程序也可以由任何语言来编写,上面的例子只是以Perl为例,你还可以用Python、Ruby、BASH脚本……,以及编译好的C/C++程序。
在上面的例子中我们还没有谈到程序的输入(input)。CGI程序使用环境变量作为输入。下面的例子展示了这一点:
#!/usr/bin/perl print "Content-type: text/plain\n", "\n"; foreach my $key (sort keys %ENV) { print "$key => $ENV{$key}\n"; }
这个CGI程序把它的环境变量和值都打印了出来。假设它对应的URL是http://localhost/cgi-bin/env.pl。我们在浏览器中访问http://localhost/cgi-bin/env.pl?name=Bob,会得到类似如下的结果:
GATEWAY_INTERFACE => CGI/1.1 HTTP_ACCEPT => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 HTTP_ACCEPT_ENCODING => gzip, deflate, sdch HTTP_ACCEPT_LANGUAGE => zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4 HTTP_CACHE_CONTROL => max-age=0 HTTP_CONNECTION => keep-alive HTTP_COOKIE => ... HTTP_HOST => localhost HTTP_UPGRADE_INSECURE_REQUESTS => 1 HTTP_USER_AGENT => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36 PATH => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin QUERY_STRING => name=Bob REMOTE_ADDR => 10.0.0.9 REMOTE_PORT => 50497 REQUEST_METHOD => GET REQUEST_SCHEME => http REQUEST_URI => /cgi-bin/env.pl?name=Bob SCRIPT_FILENAME => /var/www/cgi-bin/env.pl SCRIPT_NAME => /cgi-bin/env.pl SERVER_ADDR => 10.0.0.8 SERVER_ADMIN => webmaster@localhost SERVER_NAME => localhost SERVER_PORT => 80 SERVER_PROTOCOL => HTTP/1.1 SERVER_SIGNATURE => <address>Apache/2.4.7 (Ubuntu) Server at ubuntu-vm Port 80</address> SERVER_SOFTWARE => Apache/2.4.7 (Ubuntu) ...
其中,HTTP_开头的变量都是请求头(request header),其他大部分都是服务器提供的元变量(meta variable),如SERVER_NAME,REQUEST_URI和REMOTE_ADDR等,以及少量关于主机的环境变量,如PATH。这些变量的含义大都不言自明,在此不一一解释。值得指出的是,我们在URL请求里的查询部分,即”?name=Bob”,可以通过QUERY_STRING变量得到。此外,如果请求含有消息正文(message body),如前面“资源与方法”一节的POST的例子,我们可以通过读取标准输入设备(stdin)来得到它。
总之,CGI程序通过环境变量和标准输入获得请求的各种参数信息,通过标准输出返回应答;服务器并不关心CGI程序是用什么语言编写的,它仅通过环境变量和标准输入、输出与CGI程序交互。
每当有一个请求对应到一个CGI程序时,服务器就启动一个进程执行这个CGI程序。因此CGI程序对主机的资源消耗比较大(想想如果有1000个并发请求会怎么样),同时它的响应速度也会比较慢(进程的启动比较花时间)。所以人们开始寻找CGI的替代者,这导致了FastCGI等技术的出现。关于CGI的更多信息,可参考https://en.wikipedia.org/wiki/Common_Gateway_Interface。
严格来说Nginx不直接支持CGI,但它支持CGI的变体FastCGI,所以实际上仍然算是支持的。参考:https://www.nginx.com/resources/wiki/start/topics/examples/simplecgi/
2. 以Apache服务器为例,请参考它的文档来设置好CGI的运行环境: http://httpd.apache.org/docs/current/howto/cgi.html
感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程