💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 3.4.1 CGI ### 3.4.1 CGI CGI即**通用网关接口(Common Gateway Interface)**,1993年由美国NCSA(National Center for Supercomputing Applications)发明。它具有简单易用、语言无关的特点。虽然今天已经少有人直接使用CGI进行编程,但它仍被主流的Web服务器,如Apache、IIS、Nginx[1](#fn_1)等,所广泛支持。另外,它还影响了其他一些服务器端技术,如PHP、Python WSGI、Ruby Rack等——在这些技术里你都能看到CGI的影子。所以它仍然值得学习。 先来看一个最简单的CGI程序[2](#fn_2): ``` #!/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 - 服务器应答](response.html)一节我们已经了解了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](https://en.wikipedia.org/wiki/Common_Gateway_Interface)。 > 1. 严格来说Nginx不直接支持CGI,但它支持CGI的变体FastCGI,所以实际上仍然算是支持的。参考:<https://www.nginx.com/resources/wiki/start/topics/examples/simplecgi/>[↩](#reffn_1 "Jump back to footnote [1] in the text.") > 2. 以Apache服务器为例,请参考它的文档来设置好CGI的运行环境: <http://httpd.apache.org/docs/current/howto/cgi.html>[↩](#reffn_2 "Jump back to footnote [2] in the text.")