介绍
公共网关接口Common Gate Interface,通常简称CGI。下面是百科上面的两句话:
在物理上,CGI是一段程序,它运行在Server上,提供同客户端 Html页面的接口。
绝大多数的CGI程序被用来解释处理来自表单的输入信息,并在服务器产生相应的处理,或将相应的信息反馈给浏览器。
由此总结出了CGI的两个特点
- 服务器上的可执行程序
- 处理表单数据,然后反馈给浏览器
首先,它是可执行程序,那么自然就有多种方式,结合每个人的开发语言不同,本次介绍了四种语言版本。
- C语言版本
- Python版本
- Shell版本
- Golang版本
然后这个CGI是有输入,有输出的。
方向 | 内容 |
---|---|
输入 | 一般就是表单数据 |
输出 | 一般就是html语言,或者一些指定格式的数据,例如json等 |
一句话,它就是服务器用来接受浏览器请求,处理数据,返回结果的程序。
各种语言版本实现方法都是大同小异:
http方法 | 工作方式 |
---|---|
GET方法 | CGI程序就会从环境变量QUERY_STRING中获取数据。 |
POST方法 | Web服务器会通过stdin(标准输入)向CGI中传送数据的。而传送的数据长度就是放在ONTENT_LENGTH中的。 |
模拟了一个配置管理页面。首先介绍一下前端代码,使用的是bootstrap+js实现
html部分
<div class="container">
<h1>配置</h1>
<form class="form-horizontal">
<div class="form-group">
<label for="text_1" class="col-sm-2 control-label">文本</label>
<div class="col-sm-5">
<input type="text" class="form-control" id="text_1" placeholder="文本">
</div>
</div>
<div class="form-group">
<label for="select_1" class="col-sm-2 control-label">选择列表</label>
<div class="col-sm-5">
<select class="form-control" id="select_1">
<option value="0">禁用</option>
<option value="1">启用</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-5">
<button type="button" class="btn btn-primary" id="bAPPLY">更新</button>
</div>
</div>
</form>
</div>
两个输入,一个提交按钮,利用的ajax方式,所以没有直接form,避免了页面刷新。
js部分
function page_init()
{
var gtmstring = {"formname":"formA","action":"get_value","argument":"nul"};
$.ajax({
type: "POST",
url: "/cgi-bin/main.cgi",
contentType:"application/x-www-form-urlencoded",
data: gtmstring,
timeout:5000,
dataType: "json",
success: function(msg)
{
var all_value = msg.valuelist;
//alert(all_value);
var arry_value = all_value.split(",");
$("#text_1").val(arry_value[0]);
$("#select_1").val(arry_value[1]);
}
});
}
function page_commit()
{
if (isEmpty($('#text_1').val()))
{
alert("请输入文本");
return false;
}
var gtmstring = "a=" + $("#text_1").val();
gtmstring += ",b=" + $("#select_1").val();
var finaldata = {"formname":"formA","action":"set_value","argument":gtmstring};
$.ajax({
type: "POST",
url: "/cgi-bin/main.cgi",
contentType:"application/x-www-form-urlencoded",
data: finaldata,
dataType: "text",
timeout:5000,
success: function(msg)
{
if(msg.indexOf("ok") != -1)
{
alert("配置成功!");
}
else
{
alert("配置失败!");
}
}
});
}
采用的ajax方式提交表单,直接返回结果弹出提示。
那么就分别提供一下各种语言下的一个小例子。
C语言版本
char* getcgidata(FILE* fp, char* requestmethod)
{
char* input;
int len;
int size = 10240;
int i = 0;
if (!strcmp(requestmethod, "GET"))
{
input = getenv("QUERY_STRING");
return input;
}
else if (!strcmp(requestmethod, "POST"))
{
len = atoi(getenv("CONTENT_LENGTH"));
input = (char*)malloc(sizeof(char)*(size + 1));
if (len == 0)
{
input[0] = '\0';
return input;
}
while(1)
{
input[i] = fgetc(stdin);
if (i == size)
{
input[i+1] = '\0';
return input;
}
--len;
if (feof(fp) || (!(len)))
{
i++;
input[i] = '\0';
return input;
}
i++;
}
}
return NULL;
}
int main()
{
char *input;
char *req_method;
char formname[16];
char action[16];
char argument[256];
int i = 0;
int x = 0;
int j = 0;
req_method = getenv("REQUEST_METHOD");
input = getcgidata(stdin, req_method);
for ( i =(int)strlen("formname:"); i < (int)strlen(input); i++ )
{
if ( input[i] == '&' )
{
formname[j] = '\0';
break;
}
formname[j++] = input[i];
}
for ( i = (int)strlen("formname:")+(int)strlen("&action:")+ strlen(formname), j = 0; i < (int)strlen(input); i++ )
{
if ( input[i] == '&' )
{
action[j] = '\0';
break;
}
action[j++] = input[i];
}
strcpy(argument,&input[(int)strlen("formname:")+ (int)strlen(formname)+ (int)strlen("&action:")+(int)strlen(action)+(int)strlen("&argument:")]);
//此处增加自己的操作。
return 1;
}
这里介绍了核心部分的代码,就是从stdin中,拿到我们需要的表单名称,表单操作,以及传入的参数之后,就可以随意工作了,存入文件,存入数据库,都可以。
Python版本
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# CGI处理模块
import cgi, cgitb
# 创建 FieldStorage 的实例化
form = cgi.FieldStorage()
# 获取数据
formname = form.getvalue('formname')
action = form.getvalue('action')
argument = form.getvalue('argument')
print ("Content-Type=text/html;charset=utf-8\n\n")
if formname == 'formA':
if action == 'get_value':
print ("{\"valuelist\":\"pythontxt,0\"}")
else:
##增加自己的操作处理
print ("ok")
这个是python3.x版本的例子,python中有封装好的cgi模块,提供接口来解析post数据。这一点就很好用。并且python对于数据库及其他方法处理也是各种成熟方案都有,因此是很方便的。
Shell版本
#!/bin/bash
#test-cgi
echo "Content-Type:text/html;charset=utf-8"
echo
#echo SERVER_SOFTWARE = $SERVER_SOFTWARE #服务器软件
#echo SERVER_NAME = $SERVER_NAME #服务器主机名
#echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE #CGI版本
#echo SERVER_PROTOCOL = $SERVER_PROTOCOL #通信使用的协议
#echo SERVER_PORT = $SERVER_PORT #服务器的端口号
#echo REQUEST_METHOD = $REQUEST_METHOD #请求方法(GET/POST/PUT/DELETE..)
#echo HTTP_ACCEPT = $HTTP_ACCEPT #HTTP定义的浏览器能够接受的数据类型
#echo SCRIPT_NAME = $SCRIPT_NAME #当前运行的脚本名称(包含路径)
#echo QUERY_STRING = $QUERY_STRING #地址栏中传的数据(get方式)
#echo REMOTE_ADDR = $REMOTE_ADDR #客户端的ip
if [ "$REQUEST_METHOD" == "POST" ];then
if [ "$CONTENT_LENGTH" -gt 0 ]; then
read -n $CONTENT_LENGTH paradata <&0
fi
elif [ "$REQUEST_METHOD" == "GET" ];then
paradata=$QUERY_STRING
fi
para_array=(${paradata//&/ })
formname=${para_array[0]#*=}
action=${para_array[1]#*=}
argument=${para_array[2]#*=}
##echo "{\"valuelist\":\"$action,0\"}"
if [ "$action" == "get_value" ];then
echo "{\"valuelist\":\"shelltxt,0\"}"
elif [ "$action" == "set_value" ];then
## 此处可以增加自己的操作
echo "ok"
fi
这个是shell版本,get和post方式都有,这个的方法就很简单了,对于环境的要求不高,能支持bash就可以用,并且无需编译,也是很灵活。
Golang版本
package main
import(
"fmt"
"os"
"bufio"
"strconv"
"strings"
)
var inputReader *bufio.Reader
var input string
var err error
func init() {
fmt.Print("Content-Type=text/html;charset=utf-8\n\n");
}
func main() {
method := os.Getenv("REQUEST_METHOD")
if method == "POST" {
var datalen int
datalen,err = strconv.Atoi(os.Getenv("CONTENT_LENGTH"))
inputReader = bufio.NewReaderSize(os.Stdin,datalen)
postdata, _ := inputReader.Peek(datalen)
parms := strings.Split(string(postdata), "&")
formname := strings.Split(parms[0], "=")
actions := strings.Split(parms[1], "=")
argument := strings.Split(parms[2], "=")
if actions[1] == "get_value" {
fmt.Printf("{\"valuelist\":\"gotxt,0\"}")
}else if actions[1] == "set_value" {
/*此处增加操作*/
fmt.Printf("ok")
}
}else if method == "GET" {
getdata := os.Getenv("QUERY_STRING")
fmt.Printf("%s",getdata)
}
}
网上搜索了很久,也没找到cgi这个包怎么用的,只能自己写了一个解析过程,参考的就是前面的编程思想,golang使用的还是少一些,网上文章翻来覆去就那么几篇。可能像我这么用的人太少了吧。
各种语言比较
- Shell不挑环境,随时修改调试,适合shell高手
- Python成熟工具包丰富,接口也很全,用起来很简单,适合新手操作
- Go语言网上的资料不多,适合想挑战自己的人
- c语言似乎没什么优势,但是不妨碍它最优秀
所有代码下面提供了下载。
下载
代码结构如下
可以在build.sh中配置使用哪种语言cgi
结尾
真快,又是一周时间过去了,这周没怎么动笔,主要浪费了一些时间来收拾自己的电脑,顺带弄了一个家庭游戏机。
昨天看了谢霆锋的《怒火重案》,大厨多年不演电影了,这次带来的电影还算中规中矩,也让人感慨,香港电影的颓势依旧没办法阻挡,电影中的稍微重要一点的角色,都是大家熟悉的老面孔,大师兄林国斌,渣哥吕良伟,泰国佬卢惠光,当年也是翻来覆去这些人,如今,撑起香港电影的,还是他们,属实挺无奈的。
我们见证了香港电影的辉煌时代,从八十年代开始,成龙的动作片,发哥的警匪片,星爷的无厘头,王晶的……搞笑片。真正的百花齐放。也成了我们童年回忆不可缺少的一部分,那时候小伙伴们聚在一起,看上一部星爷的电影,也是相当的美好。
前几天看到了一个李连杰的动作集锦,也曾感慨,如今的动作片,根本无法再出现李连杰这种奇才了。一方面是传统文化的流失,一方面是生活节奏的加快,各种花瓶偶像,各种综艺选秀,哪里还能有时间再磨炼出一个功夫皇帝。
电影老了,其实我们的心也在慢慢变老,变得怀旧,变得感性,也变得宽容,变得稳重。前几天孙海洋丢失的儿子找到了,对于父亲来说,这一刻来的太晚了,已经很多年了,多到他儿子已经忘记了他的记忆,很多人说这个儿子不孝,不该替养父母说话,其实我们大可不必这样苛求一个人,突然陷入到这种巨大的家庭变故当中,而能做到客观评判,理性处理,如果能做到,我们反而应该会觉得这个人思想可怕。我们只需要把一切交给时间,当他以后有了自己的孩子,自然就会知道,他父亲坚持那么多年,为的是什么。