一、 简介
公共网关接口(Common Gateway Interface,CGI)是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据API与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。几乎所有服务器都支持CGI,可用任何语言编写CGI,包括流行的C、C ++、Java、VB 和Delphi 等。顺便说说WSGI,这个只是个规范,不是API也不是代码。
注:在测试的时候,可以预先写一个html文字段,把你不知道的字段放进去输出到网页(str()),这样可以快速定位错误
二、 启动服务器
运行该服务器只需几行代码:
# coding=utf-8
from http.server import CGIHTTPRequestHandler, test
test(CGIHTTPRequestHandler)
其默认的端口为8000,运行时可以指定端口,代码如下:
python3 cgidemo.py 8080
三、简单表单示例
文件结构:
- cgidemo.py 服务器开启文件
- friend.html 表单页面
- cgi-bin/ 脚本文件夹
- cgi-bin/friend.py 处理表单的脚本文件
代码如下:
friend.html
<html>
<body>
<title>Friends!hello!</title>
<form action="/cgi-bin/friend.py">
<p>please input your name</p>
<input type="text" name="person" id="1" value="NER USER">
<p>How many friend do you have</p>
<input type="radio" name="howmany" value="0" checked="True">0
<input type="radio" name="howmany" value="10" >10
<input type="radio" name="howmany" value="25" >25
<input type="radio" name="howmany" value="50" >50
<input type="radio" name="howmany" value="10" >100
<input type="submit">
</form>
</body>
</html>
cgi-bin/friend.py
# coding=utf-8
reshtml = '''
<html><head><title>
friend CGI Demo
</title></head>
<body><h3>Friend list for<i>%s</i></h3>
Your name is: <b>%s</b><p>You have <b>%s</b> friends.
</body></html>
'''
form = cgi.FieldStorage() //表单存储类实例化
who = form['person'].value
howmany = form['howmany'].value
print(reshtml%(who, who, howmany))
四、 高级CGI(上传,cookie)
注:CGI只支持两种表单提交和文件上传方式:1.mulitipart(上传文件专用) 2.application/x-www-form-urlencoded(默认)
cookie的设置:
cookie的设置很简单,只需要把cookie的值放在html中即可,如:
Content-Type: text/html
Set-Cookie: name="MYCGI";expires=Wed, 28 Aug 2016 18:30:00 GMT
···
其cookie保存在os.environ[‘HTTP_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.baidu.com”
- secure: 如果给出此标志,表示cookie只能通过SSL协议的https服务器来传递。
- cookie的接收是通过设置环境变量HTTP_COOKIE来实现的,CGI程序可以通过检索该变量获取cookie信息。
文件上传
上面已经说过文件上传的方式,这里强调,必须用post方式上传!否则读不到内容!
上传之后通过file.filename.value
取其文件名,通过file.file.read()
取文件内容。
粗糙的测试代码如下(半成品,还有展示错误页面,展示cookie等功能没完成):
#coding=utf-8
#this file is to show the use of cookie
from cgi import FieldStorage
from os import *
from io import StringIO
# from urllib import quote, unquote
class demoCGI:
header = 'Content-type:text/html \n'
# the script for dealing with data
url='/cgi-bin/friendB.py'
# the html 1:action 2:CPPusercookie 3:setcookitcalue 4:setname 5:langItem
formhtml = '''
<html><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><head><title>
friend CGI Demo
</title></head>
<body>
<h2>My Advance CGI Demo</h2>
<form action="%s" method="post" enctype="multipart/form-data">
<h3>My cookie Setting</h3>
<li><b>CPPuser: %s</b></li>
<h3>Enter Cookie Value: <input name="cookie" value="%s">(<i>optional</i>)</h3>
<h3>Enter Your Name : <input name="person" value="%s">(<i>require</i>)</h3>
<h3>What language can you program in?(<i>At least one require!</i>)<br>%s</h3>
<h3>Upload the File : <input name="upfile" type="file" size=45>(<i><small>Max size:4K</small></i>)</h3>
<p><input type='submit'></p>
</form>
</body></html>'''
reshtml = '''
<html><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/><head><title>
friend CGI Demo
</title></head>
<body>
<h2>You Upload the file!</h2>
<h3>Your cookie's value: %s</h3>
<h3>Your name: %s</h3>
<h3>Your language: </h3> %s
<h2>You had uploaded the file, and its name is: %s</h2>
<h3>The file's content: </h3>
%s
</body></html>'''
# the set of language and the text of checkbox
langSet = ('Python', 'Java', 'PHP', 'C++', 'Javascript', 'Go', 'Ruby')
# the first %s is for the language name and the second is for whether this language is checked or not
langItemText = "<input type='checkbox' name='lang' value='%s' %s> %s"
def buildItem(self, langSet,langItemText):
res = ""
check = "checked"
for lang in langSet:
res += langItemText % (lang, check, lang)
if check == "checked":
check = ""
return res
# to run the proc
def go(self):
# cookie store only cookie but the name
self.cookie = {}
self.error = ''
form = FieldStorage()
if not form.keys():
cookieerror = 'You have no cookie!'
cookievalue = 'Hey! Input the cookie!'
namevalue = 'Hey! Input your name!'
langhtml = self.buildItem(self.langSet, self.langItemText)
print("%s%s" % (self.header, self.formhtml % (self.url, cookieerror, cookievalue, namevalue, langhtml)))
return
if 'cookie' in form:
self.cookie['cookie'] = form['cookie'].value
if 'person' in form:
self.person = form['person'].value
else :
self.error = 'Your name is required!'
if 'lang' in form:
langData = form['lang']
# 判断是否类型相同
if isinstance(langData, list):
self.langs = [eachlang.value for eachlang in langData]
else:
self.langs = [langData.value]
else:
self.error = "At least one language required!"
self.file = ""
if 'upfile' in form:
upfile = form['upfile']
# Upfile or not
self.fn = upfile.value
self.file = str(upfile.file.read(4096))
if self.error == '':
self.doResult()
else:
self.go()
# read Cookie form client
def getCPPcookie(self):
if 'HTTP_COOKIE' in environ:
print(environ['HTTP_COOKIE'])
def setCPPcookie(self):
if self.cookie:
tmpcookie = "cookie=" + self.cookie['cookie']
return "Set-Cookie: %s"%(tmpcookie)
def doResult(self):
# cookie
cookieItem = self.setCPPcookie()
# name
personItem = self.person
# language item
langItem = "<ul>"
for eachlang in self.langs:
langItem += '<li>' + eachlang + '</li>'
langItem += '</ul>'
# file
print ('Content-Type: text/html')
print (cookieItem)
print(self.reshtml%(self.cookie['cookie'],personItem,langItem,self.fn,self.file))
if __name__ == '__main__':
page = demoCGI()
page.go()