CGI(Common Gateway Interface)也叫通用网关接口,它是一个web服务器主机提供信息服务的标准接口,只要遵循这个接口,web服务器就能获取客户端提交的信息,转交给服务端的CGI程序进行处理,然后将处理结果返回给客户端。
CGI通讯由两部分组成:一部分是用户的浏览器显示的页面,也就是html页面,另一部分是运行在服务器上的CGI程序。
CGI是Web服务器和一个独立的进程之间的协议,它会把HTTP请求 Request 的 Header 头设置成进程的环境变量,HTTP 请求的 Body 正文设置成进程的标准输入,进程的标准输出设置为HTTP响应 Response,包含 Header 头和 Body 正文。
本例使用Apache2.4.x + Python3.x + windows搭建Web服务器实现CGI编程
官方下载地址:Apache2.4.x下载地址
需要:VC14 需要下载Microsoft Visual C++ 2015,官方地址为:Microsoft Visual C++ 2015
A、配置Apache文件的根目录:设置SRVROOT根目录为电脑端Apache的存放路径。为方便后期修改方便,可利用引用“${SRVROOT}”的方式来书写配置,即:
注:Apache2.2.x版本修改“ServerRoot”
B、修改端口配置:默认端口80,此处改为1024
C、设置python文档的存储路径,以便Python解释器能够找到它们(即设置访问权限),此处路径设置为“D:\Apache24\cgi-bin”,找到如下内容:
修改为:
配置文件:
<Directory "${SRVROOT}/cgi-bin">
AllowOverride None
Options Indexes FollowSymLinks ExecCGI
Require all granted
Require host ip
</Directory>
注意:httpd.conf里面还有一个标签,此处不用改:
D、添加.py 后缀:在 AddHandler 中添加 .py 后缀,这样就可以访问 .py 结尾的 python 脚本文件
E、开启cgi_module modules/mod_cgi.so模块(即去掉模块前的注释#):
F:启动apach服务器(Apache24\bin目录下)
Apache24\bin目录下,使用“httpd -k install -n apache”安装Apache主服务,然后输入“httpd –k start”开启Apache服务(管理员身份运行DOS)
G:编写.py脚本
#!D:/Python/Python37/python.exe
# -*- coding: utf-8 -*-
print ("Content-type:text/html\n\n")
print # 空行,告诉服务器结束头部
print ('')
print ('')
print ('')
print ('this ia a CGI program !!! ')
print ('')
print ('')
print ('this ia a CGI program !!!
')
print ('')
print ('')
脚本说明:
1、第一行是申明电脑端解释器的位置,注意一定要写第一行,这是python的格式规范
2、因电脑端安装的是Python3.7,输出语句print("")一定要加括号,一定要注意,不然之后会报500错误
3、第三行的print (“Content-type:text/html\n\n”),这是声明格式,此处注意,其中的“\n\n”,两个“\n”,一个都不能少,少了也会报500错误
4、ScriptAlias /cgi-bin/ "${SRVROOT}/cgi-bin/"设置了解释器查找.py文件的方法
H:浏览器输入“http://localhost:1024/cgi-bin/hello.py”即可运行hello.py文件
I:修改cgi-bin中.py文件路径为任意位置:修改httpd.conf中和ScriptAlias下路径
J:修改Apache中.html文件存放路径:修改httpd.conf中DocumentRoot和的配置,即:
上文中hello.py文件中的" Content-type:text/html"为HTTP头部的一部分,它会发送给浏览器告诉浏览器文件的内容类型。
HTTP头部格式:HTTP 字段名: 字段内容
如:Content-type:text/html
CGI程序中HTTP头部经常使用的信息如下:
头部信息 | 描述 |
---|---|
Content-type: | 请求的与实体对应的MIME信息,例如: Content-type:text/html |
Expires: Date | 响应过期的日期和时间 |
Location: URL | 用来重定向接收方到非请求URL的位置来完成请求或标识新的资源 |
Last-modified: Date | 请求资源的最后修改时间 |
Content-length: N | 请求的内容长度 |
Set-Cookie: String | 设置Http Cookie |
所有的CGI程序都会接收以下环境变量,这些环境变量在CGI程序中发挥了重要的作用,具体如下:
环境变量名 | 描述 |
---|---|
CONTENT_TYPE | CONTENT_TYPE的值指示所传递来的信息的MIME类型。一般情况下该变量的值是:application/x-www-form-urlencoded,表示数据来自于HTML表单 |
CONTENT_LENGTH | 假如服务器与CGI程序信息的传递方式是POST,这个环境变量即从标准输入STDIN中可以读到的有效数据的字节数。这个环境变量在读取所输入的数据时必须使用。 |
HTTP_COOKIE | 客户机内的 COOKIE 内容 |
HTTP_USER_AGENT | 提供包含了版本数或其他专有数据的客户浏览器信息 |
PATH_INFO | 这个环境变量的值表示紧接在CGI程序名之后的其他路径信息。它常常作为CGI程序的参数出现。 |
QUERY_STRING | 如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值即所传递的信息。这个信息经跟在CGI程序名的后面,两者中间用一个问号’?'分隔。 |
REMOTE_ADDR | 这个环境变量的值是发送请求的客户机的IP地址,例如上面的192.168.1.67。这个值总是存在的。而且它是Web客户机需要提供给Web服务器的唯一标识,可以在CGI程序中用它来区分不同的Web客户机。 |
REMOTE_HOST | 这个环境变量的值包含发送CGI请求的客户机的主机名。如果不支持你想查询,则无需定义此环境变量。 |
REQUEST_METHOD | 提供脚本被调用的方法。对于使用 HTTP/1.0 协议的脚本,仅 GET 和 POST 有意义。 |
SCRIPT_FILENAME | CGI脚本的完整路径 |
SCRIPT_NAME | CGI脚本的的名称 |
SERVER_NAME | 这是你的 WEB 服务器的主机名、别名或IP地址。 |
SERVER_SOFTWARE | 这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(Unix) |
#!D:/Python/Python37/python.exe
import os
# -*- coding: utf-8 -*-
print ("Content-type:text/html\n\n")
print # 空行,告诉服务器结束头部
print ('')
print ('')
print ('')
print ('this ia CGI environment variable ')
print ('')
print ('')
print ('this ia CGI environment variable
')
print (""
)
for key in os.environ.keys():
print ("%30s : %s " % (key,os.environ[key]))
print ("")
print ('')
print ('')
浏览器输入“http://localhost:9999/cgi-bin/ZX_02_Environment_Variable.py”,结果如下:
GET方法发送编码后的用户信息到服务端,数据信息包含在请求页面的URL上,以"?"号分割
GET请求需注意如下内容:
1、GET 请求可被缓存
2、GET 请求保留在浏览器历史记录中
3、GET 请求可被收藏为书签
4、GET 请求不应在处理敏感数据时使用
5、GET 请求有长度限制
6、GET 请求只应当用于取回数据
步骤:
1、导入cgi库
import cgi
2、创建 FieldStorage 的实例化
form = cgi.FieldStorage()
3、通过表单获取数据
key = form.getvalue('value')
get_url.py文件内容:
#!D:/Python/Python37/python.exe
# 导入cgi处理模块
import cgi
# 实例化FieldStorage,代表整个表单
form = cgi.FieldStorage()
# 通过表单获取数据
name = form.getvalue("name")
pwd = form.getvalue("pwd")
print("Content-type:text/html\n\n")
print
print('')
print('')
#print('') 注释掉该行后web显示可正常显示中文
print('这是网址的title ')
print('')
print("")
print('姓名:%s
' % name)
print('密码:%s
' % pwd)
print("")
print('')
浏览器输入“http://localhost:9999/cgi-bin/get_url.py?name=june&pwd=aaaaaaaa”后web页面显示
get_url.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>白杨(Gavin.ZX.com)</title>
</head>
<body>
<form action="/cgi-bin/ZX_03_GET_URL.py" method="get">
用户名: <input type="text" name="name"> <br />
密码: <input type="text" name="pwd" />
<input type="submit" value="提交" />
</form>
</body>
</html>
get_url.py文件内容:
#!D:/Python/Python37/python.exe
# 导入cgi处理模块
import cgi , cgitb
# 实例化FieldStorage,代表整个表单
form = cgi.FieldStorage()
# 通过表单获取数据
name = form.getvalue("name")
pwd = form.getvalue("pwd")
print("Content-type:text/html\n\n")
print
print('')
print('')
#print('') 注释掉该行后web显示可正常显示中文
print('这是网址的title ')
print('')
print("")
print('姓名:%s
' % name)
print('密码:%s
' % pwd)
print("")
print('')
浏览器输入“http://localhost:9999/get_url.html”,结果如下:
Python使用get方式传输html表单
post_url.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>白杨(Gavin.ZX.com)</title>
</head>
<body>
<form action="/cgi-bin/ZX_04_POST_URL.py" method="post">
用户名: <input type="text" name="name"> <br />
密码: <input type="text" name="pwd" />
<input type="submit" value="提交" />
</form>
</body>
</html>
post_url.py文件内容:
#!D:/Python/Python37/python.exe
# 导入cgi处理模块
import cgi , cgitb
# 实例化FieldStorage,代表整个表单
form = cgi.FieldStorage()
# 通过表单获取数据
name = form.getvalue("name")
pwd = form.getvalue("pwd")
print("Content-type:text/html\n\n")
print
print('')
print('')
#print('') 注释掉该行后web显示可正常显示中文
print('POST方法传输HTML数据 ')
print('')
print("")
print('姓名:%s
' % name)
print('密码:%s
' % pwd)
print("")
print('')
浏览器输入“http://localhost:9999/post_url.html”,结果如下:
python使用post传输html数据
checkbox通常用于提交一个或者多个选项数据,其HTML文件如下:
CheckBox.html文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>利用CGI传输CheckBox数据</title>
</head>
<body>
<form action="/cgi-bin/ZX_05_CheckBox.py" method="POST" target="_blank">
<input type="checkbox" name="baidu" value="on" /> 百度
<input type="checkbox" name="google" value="on" /> 谷歌
<input type="submit" value="选择站点" />
</form>
</body>
</html>
CheckBox.py文件:
#!D:/Python/Python37/python.exe
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('google'):
google_flag = "是"
else:
google_flag = "否"
if form.getvalue('baidu'):
baidu_flag = "是"
else:
baidu_flag = "否"
print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("利用CGI传输CheckBox数据 ")
print ("")
print ("")
print (" 百度被选择 : %s
" % baidu_flag)
print (" 谷歌被选择 : %s
" % google_flag)
print ("")
print ("")
浏览器输入“http://localhost:9999/checkbox.html”,结果如下:
python使用CGI程序传输checkbox数据
Radio 仅能向服务器传递一个数据
Radio.html文件为:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>利用CGI传输Radio数据</title>
</head>
<body>
<form action="/cgi-bin/ZX_06_Radio.py" method="POST" target="_blank">
<input type="radio" name="site" value="baidu" /> Baidu
<input type="radio" name="site" value="google" /> Google
<input type="submit" value="选择站点" />
</form>
</body>
</html>
Radio.py文件:
#!D:/Python/Python37/python.exe
# 引入 CGI 处理模块
import cgi, cgitb
# 创建 FieldStorage的实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue('site'):
site = form.getvalue('site')
else:
site = "空"
print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("利用CGI传输Radio数据 ")
print ("")
print ("")
print (" 选中的网站是 %s
" % site)
print ("")
print ("")
浏览器输入“http://localhost:9999/radio.html”,结果如下:
CGI程序传递Radio数据
Textarea 可向服务器传递多行数据
Textarea.html文件:
通过CGI程序传递 Textarea数据
Textarea.py文件:
#!D:/Python/Python37/python.exe
import cgi , cgitb
# 创建FieldStorage实例
form = cgi.FieldStorage()
# 接收字段数据
if form.getvalue("textcontent"):
content = form.getvalue("textcontent")
else:
content = "没有内容"
print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("通过CGI程序传递Textarea数据 ")
print ("")
print ("")
print (" 通过CGI程序传递Textarea数据是:%s
" % content)
print ("")
print ("")
浏览器输入“http://localhost:9999/Textarea.html”,结果如下:
CGI程序传递Textarea数据
dropdown.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CGI程序传递下拉数据</title>
</head>
<body>
<form action="/cgi-bin/ZX_08_Dropdown.py" method="post" target="_blank">
<select name="dropdown">
<option value="baidu" selected>百度</option>
<option value="google">谷歌</option>
</select>
<input type="submit" value="提交"/>
</form>
</body>
</html>
dropdown.py文件:
#!D:/Python/Python37/python.exe
import cgi , cgitb
# 创建FiledStorage对象
form = cgi.FieldStorage()
# 接受数据
if form.getvalue("dropdown"):
content = form.getvalue("dropdown")
else:
content = "空"
print ("Content-type:text/html")
print ()
print ("")
print ("")
#print ("")
print ("CGI程序传递下拉数据 ")
print ("")
print ("")
print (" 选中的选项是:%s
" % content)
print ("")
print ("")
浏览器输入“http://localhost:9999/dropdown.html”,结果如下:
CGI程序传递下拉数据
HTTP 协议一个最大的缺点是不对用户身份进行校验,cookie 功能的出现一定程度上弥补了这个不足。
cookie 是在客户访问脚本的同时,通过客户的浏览器,在客户硬盘上写入纪录数据 ,当下次客户访问脚本时取回数据信息,从而达到身份判别的功能。
cookie 常用在身份校验中。
http 协议 cookie 的发送是通过 http 头部来实现的,其早于文件的传递,头部 set-cookie 的语法如下:
Set-cookie:name=name;expires=date;path=path;domain=domain;secure
参数说明:
name=name:需要设置cookie的值(name不能使用";“和”,"号),多个name值时用 “;” 分隔,例如:name1=name1;name2=name2;name3=name3。
expires=date:cookie的有效期限,格式: expires=“Wdy,DD-Mon-YYYY HH:MM:SS” 。
path=path:设置cookie支持的路径,如果path是一个路径,则cookie对这个目录下的所有文件及子目录生效,例如:path="/cgi-bin/";如果path是一个文件,则cookie指对这个文件生效,例如:path="/cgi-bin/cookie.cgi"。
domain=domain:对cookie生效的域名,例如:domain=“www.runoob.com”
secure:如果给出此标志,表示cookie只能通过SSL协议的https服务器来传递。
cookie的接收是通过设置环境变量HTTP_COOKIE来实现的,CGI程序可以通过检索该变量获取cookie信息。
Cookie的设置较为简单,cookie通常会在http头部单独发送
cookie_set.py文件:
#!D:/Python/Python37/python.exe
print ('Content-Type: text/html')
print ('Set-Cookie: name="cookie";expires=Wed, 08 July 2020 15:30:00 GMT')
print ()
print ("""
cookie设置
Cookie set OK ~~~
""")
浏览器输入“http://localhost:9999/cgi-bin/ZX_09_Cookie_set.py”,结果如下:
Cookie设置
Cookie信息存储在CGI的环境变量HTTP_COOKIE中,存储格式如下:
key1=value1;key2=value2;key3=value3....
cookie_get.py文件:
#!D:/Python/Python37/python.exe
import os , http.cookies
print("Content-type: text/html")
print()
print("""
cookie检索
cookie检索
""")
if 'HTTP_COOKIE' in os.environ:
# 获取cookie字符串
cookie_string = os.environ.get('HTTP_COOKIE')
# 创建Cookie.SimpleCookie对象
cookie = http.cookies.SimpleCookie()
cookie.load(cookie_string)
try:
data=cookie["name"].value
print('cookie data: %s
' % data)
except KeyError:
print("cookie 没有设置或者已过期
")
print("""
""")
浏览器输入“http://localhost:9999/cgi-bin/ZX_10_Cookie_get.py”,结果如下:
Cookie信息读取
HTML设置上传文件的表单需要设置 enctype 属性为 multipart/form-data
Upload.py文件:
#!D:/Python/Python37/python.exe
import cgi, os
form = cgi.FieldStorage()
# 获取文件名,返回FieldStorage类型列表,形如:FieldStorage('filename', 'test.txt', b'Test')
field_item = form['filename']
# 检测文件是否上传,field_item.filename返回文件名称
if field_item.filename:
# 设置文件路径,os.path.basename()返回path最后的文件名
filename = os.path.basename(field_item.filename)
open('E:/Mine/Python/Runoob/ZX_24_CGI/Upload/' + filename , 'wb').write(field_item.file.read())
message = "文件" + filename + "上传成功"
else:
message = '文件没有上传'
print ("""\
Content-Type: text/html\n
CGI文件上传
%s
%s
%s
""" % (message,field_item,field_item.filename,))
Upload.html文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CGI文件上传</title>
</head>
<body>
<form enctype="multipart/form-data"
action="/cgi-bin/ZX_11_UploadFile.py" method="post">
<p>选中文件: <input type="file" name="filename" /></p>
<p><input type="submit" value="上传" /></p>
</form>
</body>
</html>
浏览器输入“http://localhost:9999/Upload.html”,结果如下:
CGI上传文件
文件下载通过设置HTTP头信息来实现
参考文档:CGI实现文件上传和下载
DownloadFile.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="gbk">
<title>CGI上传和下载文件</title>
</head>
<body>
<form enctype="multipart/form-data"
action="/cgi-bin/ZX_12_DownloadFile.py" method="post">
<p><input type="button" value="下载" onclick="location.href='/cgi-bin/ZX_12_DownloadFile.py'" method="get"></p>
</form>
</body>
</html>
DownloadFile.py文件
#!D:/Python/Python37/python.exe
import os , sys
#下载文件
# 文件路径 Download.txt 赋值给file_path
Downloadfile_path = 'E:/Mine/Python/Runoob/ZX_24_CGI/Upload/pytest.pdf'
# 文件名赋值给 file_name
file_name=os.path.basename(Downloadfile_path)
# 获取文件大小,并赋值给file_size
file_size = os.path.getsize(Downloadfile_path)
# HTTP 头部,设置下载文件名称
print ('Content-Disposition: attachment; filename = 下载.pdf')
# 这个是文件下载框显示的文件大小
print('Accept-Length:%s'%(file_size))
#输出1行空行,告诉浏览器HTTP头部结束
print ()
# 以读取二进制的方式打开一个文件,返回的文件描述符,赋值给fd
file = open(Downloadfile_path, "rb")
#通过文件描述符 file 读取打开文件的数据,并赋值给 read_bytes
read_bytes = file.read()
#刷新标准输出流的缓冲区,如果不刷新的话,客户端就接收不到数据
sys.stdout.flush()
#把读取到的数据写入到标准输出流的缓冲区
sys.stdout.buffer.write (read_bytes)
#关闭文件描述符fd
file.close()
浏览器输入“http://localhost:9999/DownloadFile.py”,结果如下:
CGI下载文件