我对CGI的了解

CGI的出现

CGI算是一门古老的技术,它是在上个世纪90年代出现的,在当时Web只有静态网站的情况下提出了动态网站的需求,CGI技术就是为了满足这种需求而出现的。

当时Web站点只有静态文件,静态网页是没有变化的,而如果需要服务器能够能够拓展更多的功能,例如表单处理,访问数据库并处理返回的数据,制作动态网站等等,那么就需要一种程序语言编写相应的功能,但这种程序如何与静态文件html以及服务器交互,共同完成一份HTTP响应报文给浏览器?这就是CGI的作用。

CGI

通用网关接口(Common Gateway Interface, CGI)是Web服务器运行时外部程序的规范,利用CGI编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据API与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。几乎所有服务器都支持CGI,编写CGI的语言可以是C、Java、Perl、Python等。

  • “通用”:只要是支持获取环境变量和输入输出的语言都能用来编写CGI程序。

  • “网关”:网关具有两个不同实体(协议、程序等)之间的转换功能。

  • “接口”:两个实体是通过接口来实现数据交互,CGI的接口是环境变量和标准输入输出,即HTTP服务器往其中写入或读取数据,CGI程序也是往其中读取或写入数据。

CGI通信模型

在这里插入图片描述

我把这个模型分为三个阶段。

准备阶段

当浏览器发送一个CGI程序的URL请求时,URL的域名部分被定位到服务器地址,服务器接收到请求后,解析URL的路径部分,并定位到文件系统中相应的文件,服务器发现这是一个CGI程序,服务器就为它创建一个进程,并解析HTTP报文中的数据,例如表单或查询字符串,然后将其往进程(PCB)中写入环境变量和标准输入中。

如果是GET请求,URL的query部分被解析后,将其写入到QUERY_SRING的环境变量中。

如果是POST请求,就将报文体中的数据写入到标准输入中。

执行阶段

当服务器为CGI程序创建好一个进程并初始化变量后,CGI程序开始执行,前面说过CGI程序可以是C、Python、Java等语言编写,那么CGI定义所说的“服务器扩展功能”也是用这些编程语言实现,例如在python实现的CGI程序中,先用cgi模块读取环境变量和标准输入,然后再调用mysql模块访问MySQL数据库,然后再对返回的数据做进一步处理。

又如定义所说的“动态网站”也体现在这里,例如CGI程序可以利用从数据库得到的数据,对静态文件HTML做修改。

输出阶段

事实上,文件就是数据的集合,静态网站中服务器返回一个HTML文件,返回的是文件中的数据,所以用CGI实现的动态网站中,也是返回数据,只不过返回的数据可能有所变化。CGI程序最后在标准输出中输出数据,服务器把这些数据重定向到socket中,最终返回给浏览器。

环境变量

当一个CGI程序不是被HTTP服务器调用时,它的环境变量几乎时系统环境变量的复制。

当一个CGI程序被HTTP服务器调用时,它的环境变量会增加关于HTTP服务器、客户端、HTTP请求等相关项。

可以分为三类:与请求相关的、与服务器相关的、与客户端相关的。

列举一部分环境变量:

变量名含义
REQUEST_METHODHTTP请求方法
QUERY_STRING采用GET方式时URL的查询字符串
CONTENT_LENTGHstdio中有效信息长度(即请求报文体中的数据长度)

更多环境变量可在网上查找资料。

URL编码

不管是POST或GET方式,浏览器发送给服务器的数据都不是原生的字符数据,而是经过URL编码的。HTTP请求的URL部分是URL编码的,而表单数据也是经过URL编码的,这时"Content-Type"的值为"application/x-www-form-urlencode"(MIME类型)。

URL编码规则:

(1)变量之间用"&"分开;

(2)变量与其对应的值用"="连接;

(3)空格用"+"代替;

(4)保留的控制字符用"%"拼接对应的16进制ASCII码代替;

(5)某些非打印字符也可以用"%"拼接对应的16进制ASCII码代替;

(6)空格是非法字符;

在CGI程序从标准输入或环境变量中读取变量中,某些语言有相应的模块或函数,例如python:

import cgi

form = cgi.FieldStorage()
name = form.getvalue("username")  # username为变量名

CGI输出

CGI程序的标准输出stdout被服务器重定向到socket中,所以CGI程序可以用"print"输出函数生成的新的HTML页面。但需注意的是,这里的CGI输出的数据将会是HTTP响应报文的数据,而不仅仅是响应报文体部分的数据,因此HTTP响应的header部分也需要"print"输出。

CGI提供三个响应头部:

header描述
Content-type输出数据的MIME类型
LocaitonURL重定向地址
StatusHTTP状态码

python程序实例:

import cgi

form = cgi.FieldStorage()
name = form.getvalue("username")
print("Content-Type: text/html;charset=utf-8")
print("")                 # HTTP头部与体部分之间的一个空白行
print("<h1>%s</h1>" % name)

配置Apache使用CGI

很多服务器支持CGI,包括Apache,接下来配置Apache使用CGI:

(1)打开httpd.conf配置文件,找到CGI脚本的目录配置:

#
# "${SRVROOT}/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
#
<Directory "${SRVROOT}/cgi-bin">
    AllowOverride None
    Options None
    Require all granted
</Directory>

(2)修改CGI脚本的根目录路径,并做如下配置:

<Directory "D:/phpStudy/WWW/webpy">
    AllowOverride None
    Options +ExecCGI
    Order allow,deny
    Allow from all
</Directory>

(3)修改"Addhandler",这样一来,.py和.cgi文件被访问时,就会当作程序执行。

AddHandler cgi-script .cgi .py

CGI程序

不同语言编写的CGI程序后缀名,C语言使用.cgi,python使用.py,Perl使用.pl。

C语言实现的一个CGI程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int get_inputs(){
    int length;
    char *method;
    char *inputstring;
    
    method  = getenv("REQUEST_METHOD");
    if(method == NULL)
        return 1;
    if(!strcmp(method, "POST")){
        // 读取保准输入的数据
        length = atoi(getenv("CONTENT-LENGTH"));
        if(length != 0){
            inputstring = malloc(sizeof(char)*length + 1);
            fread(inputstring, sizeof(char), length, stdin);
        }
    } else if(!strcmp(method, "GET")){
        // 读取URL的query部分
        inputstring = getenv("QUERY_STRING");
        length = strlen(inputstring);
    }
    if(length == 0)
        return 0;
}

我曾试图用python的CGI开发一个小型的项目管理系统,但实现的过程非常繁琐,还要重新造一堆轮子,而且这些轮子还需要自己重新组装,整个过程非常痛苦,而且开发效率还很低下,自那以后深刻地领悟到框架真的是个好东西。

一个python实现的CGI程序:

#!T:\Python38\python.exe
#coding: utf-8

import sys
import cgi
import cgitb
import codecs
# Python3.6 及以后版本中文乱码解决
sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer)

# CGI模块
# 创建FieldStorage实例化
form = cgi.FieldStorage()
# #  获取数据
site_name = form.getvalue("username")
site_url = form.getvalue("password")

print("Content-type:text/html")
print()
print("<html>")
print("<head>")
print("<meta charset=\"utf-8\">")
print("<title>菜鸟教程 CGI 测试实例</title>")
print("</head>")
print("<body>")
print("<h2>%s官网:%s</h2>" % (site_name, site_url))
print("</body>")
print("</html>")

访问编写的CGI程序的时候经常遇到 "500 内部错误"的问题,根据我的经验,可能的原因是:

(1)python脚本语法错误或者其它执行异常;

(2)python脚本中没有"print"输出,服务器没有得到CGI程序返回的数据,它就认为脚本的执行发生了错误,响应500;

如果你用CGI实现一些只是阅读文章的Web网站倒是足够了,但我们知道HTTP是一个无状态协议,而COOKIE可以存储用户的状态,想要完成一个有登录功能的Web网站,还需要引入COOKIE模块,python3的cookie模块是http.CookieJar。

在从http请求的cookie头部中获取数据时,还需要判断环境变量"HTTP_COOKIE"是否存在,因此要导入os模块,并用下面语句判断:

import os
if "HTTP_COOKIE" in os.environ:
	pass

CGI的问题

CGI会影响Web服务器的性能,因为每一次访问CGI程序,都会创建一个进程,这需要占用CPU的时间,大量的CGI进程会消耗很多系统资源,导致服务器性能下降。除此之外,还有多进程同步访问文件的问题。因此,种种不方便处理的问题导致CGI技术现在已经很少使用了。

资料:

CGI详解(原理,配置及访问)

CGI-百度百科

Python3 CGI编程

万法归宗CGI–知乎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值