http://book.csdn.net/bookfiles/399/index.html
CGI,公用网关接口,是一种提供动态网站内容的方法。CGI脚本可以运行在任何支持CGI的Web服务器上,并且CGI脚本是可以由任何语言编写的。CGI既不是一种网络协议,也不是它本身的一种库。而是一种说明信息是如何在Web服务器和产生数据的程序之间交换信息的说明书。
几乎所有流行的Web服务器和编程语言都支持CGI。然而,它也有一些问题,最显著的就是性能。如果需要访问数据库的话,性能会更差。
Python的CGIHTTPServer模块提供了一个运行服务器便捷的方法。
CGI脚本可以访问到客户端表单的信息。接着它会产生一个HTML文档,并在标准输出上输出。Web服务器会确保这个输出被发送给客户端。
下面这些不寻常的情况会对Python程序员有些影响:
- 初始化时间变得至关重要。多数Python程序的初始化效率都不高。本来这通常不是问题,因为这对连续运行3个月的程序来说,不在乎多等几秒钟,但是对于CGI脚本,这是一个非常重要的问题。
- 不同的错误处理。如果一个CGI脚本出现了异常,错误一般会记入Web服务器的日志中,但是客户端会收到错误信息或一个不完整的文档,而不是关于堆栈的追踪。因此错误不会对其他运行的CGI脚本产生影响。
- 交互的处理不同。CGI脚本必须根据它收到的参数而一次执行完,而不是像标准交互程序那样可以给用户提供更多的信息。如果想得到更多的信息,就必须再次调用CGI。
#!/usr/bin/env python
# Simple CGI Example - Chapter 18 - simple.cgi
import cgitb
cgitb.enable()
import time
print "Content-type: text/html"
print """<HTML>
<HEAD>
<TITLE>Sample CGI Script</TITLE>
</HEAD>
<BODY>
The present time is %s.
</BODY>
</HTML>""" %time.strftime("%I:%M:%S %p %Z")
### 简单的print到stdout就可以了。
import cgi
cgi.print_environ()
这些都可以通过os.environ得到。例如,os.environ['REMOTE_ADDR']包含客户端的IP地址。
1。URL的额外部分
通过PATH_INFO获取URL的额外部分
生成的URL
print '<AHREF="%s/%d/%d">%s</A><BR>' %(os.environ['SCRIPT_NAME'],month, code, name)
URL的解析
os.getenv('PATH_INFO', '').split('/')[1:]
2。GET
对于cgi库来说,看上去数据就像是从表单提交上来的。为了访问表单或取得来自GETURL的信息,需要使用cgi.FieldStorage()类。cgi库会自动通过FieldStorage()实例来解析输入并把它们转换成方便的变量。表单通常(但也不是全部)使用key和value对,在这个例子中,getfirst()被用来从特殊的key里面取得值(value)。
FieldStorage实例通常使用下面两个方法:getfirst()和getlist()。还可以把它们当dictionary访问。
生成的URL
print'<A HREF="%s?month=%d&day=%d">%s</A><BR>' % (os.environ['SCRIPT_NAME'], month, code, name)
URL的解析使用了cgi的FieldStorage
form = cgi.FieldStorage()
if form.getfirst('month') == None:
print_month_quiz()
FieldStorage实例通常使用下面两个方法:getfirst()和getlist()。还可以把它们当dictionary访问
3。POST
POST方法被专门用来接收HTML表单提交的数据。POST通常用来处理大的数据,包括上传的文件。
POST数据并不会出现在URL上。
表单的生成
print '<FORMMETHOD="POST" ACTION="%s">' % os.environ['SCRIPT_NAME']
print '<INPUTTYPE="hidden" NAME="month" VALUE="%d">' %month
for code, name in daymap.items():
print'<INPUT NAME="day" TYPE="radio" VALUE="%d">%s<BR>' % /
(code, name)
print '<INPUTTYPE="submit" NAME="submit" VALUE="Next>>">'
print "</FORM>"
表单的解析类似GET
form = cgi.FieldStorage()
if form.getfirst('month') == None:
print_month_quiz()
在HTML文档中,字符“<”、“>”和“&”不能被直接插入到文档中。相反,为了显示这些字符,您必须使用“<”、“>”和“&”。有两个方法可以帮助您处理特殊字符:cgi.escape()和urllib.quote_plus()。
escape(s, quote=None)
Replace special characters '&', '<' and '>' by SGML entities.
quote_plus(s, safe='')
Quote the query fragment of a URL; replacing ' ' with '+'
输入<&?+">
CGI脚本
print '<AHREF="%s?data=%s"><TT>%s</TT></A><P>' % /
(os.environ['SCRIPT_NAME'],
urllib.quote_plus(form.getfirst('data')),
cgi.escape(form.getfirst('data')))
生成的HTML
<AHREF="/cgi-bin/escape.cgi?data=%3C%26%3F%2B%22%3E+test"> #urllib.quote_plus()
<TT><&?+">test</TT></A><P> # cgi.escape()
>>> a = '<&?+>'
>>> print cgi.escape(a)
<&?+>
>>> print urllib.quote_plus(a)
%3C%26%3F%2B%3E
在HTML的表单中可以为一个名称设置多个值。您可以使用checkbox或多选框。cgi模块通过FieldStorage实例中的getlist()方法来处理这种情况。它可以返回一个列表,其中含有某个特殊字段的所有值。
#!/usr/bin/env python
# CGI list example - Chapter 18 - list.cgi
import cgitb
cgitb.enable()
import cgi, os, urllib
print "Content-type: text/html"
print """<HTML>
<HEAD>
<TITLE>CGI ListExample</TITLE></HEAD><BODY>"""
form = cgi.FieldStorage()
print "You selected: "
selections = form.getlist('data')
printable = [cgi.escape(x) for x in selections]
print ", ".join(printable)
print """<FORMMETHOD="GET" ACTION="%s">
Select some things:<P>""" %os.environ['SCRIPT_NAME']
for item in ['Red', 'Green', 'Blue', 'Black','White', 'Purple',
'Python', 'Perl', 'Java','Ruby', 'K&R', 'C++', 'OCaml', 'Haskell',
'Prolog']:
print '<INPUTTYPE="checkbox" NAME="data" VALUE="%s">' % cgi.escape(item)
print ' %s<BR>' %cgi.escape(item)
print """<INPUTTYPE="submit" NAME="submit" VALUE="Submit">
</FORM>
</BODY></HTML>"""
#!/usr/bin/env python
# CGI file example - Chapter 18 - file.cgi
import cgitb
cgitb.enable()
import cgi, os, urllib, md5
print "Content-type: text/html"
print """<HTML>
<HEAD>
<TITLE>CGI FileExample</TITLE></HEAD><BODY>"""
form = cgi.FieldStorage()
if form.has_key('file'):
fileitem = form['file']
if not fileitem.file:
print"Error: not a file upload.<P>"
else:
print"Got file: %s<P>" % cgi.escape(fileitem.filename)
m =md5.new()
size =0
while1:
data = fileitem.file.read(4096)
if not len(data):
break
size += len(data)
m.update(data)
print"Received file of %d bytes. MD5sum is %s<P>" % /
(size, m.hexdigest())
else:
print "No filefound.<P>"
print """<FORMMETHOD="POST" ACTION="%s"enctype="multipart/form-data">
File: <INPUT TYPE="file"NAME="file">
""" % os.environ['SCRIPT_NAME']
print """<INPUTTYPE="submit" NAME="submit" VALUE="Submit">
</FORM>
</BODY></HTML>"""
注意:FROM中的METHOD="POST" 和enctype="multipart/form-data"是必须的。
在这里,fileitem.file(等同于form['file'].file)用来完成这个任务。浏览器提供的文件名就包含在文件名属性中。
CGI的作者经常需要跟踪用户的session。session通常是用户和站点之间连续的交互。
HTTP本身就不是一个常连接的协议,也就是说,用户浏览的每一页都有自己的session,且在不同页之间是没有内置的方法来把它们关联起来的。但是,CGI作者经常需要这个关联。例如:如果您正在开发一个购物站点,您就需要跟踪用户在该站点上的操作,以确保用户什么时候点击了“加入购物车”(Add toCart),以及什么时候用户正在使用购物车。这种请求通常需要某种永久的保存——一般是使用数据库来保存session和购物车信息。
有几种跟踪session信息的方法。例如:您可以使用HTTP认证。如果用户只有登录后才能访问您的站点,而您又使用了HTTP认证,您就可以通过访问REMOTE_USER这个环境变量来得到认证过的用户名,并把它作为一个session记号。
另外一种方法是传递一个session记号。这个记号也许是随机产生的。它在每一个表单中都是一个内嵌的隐藏字段,或者是包含在每一个URL链接的结尾。这样虽然是可以的,但是确实非常麻烦。
现今,很多开发者都喜欢使用cookie机制。Cookie是保存在用户机器上的小记号。当用户访问您站点的时候,浏览器将把上次保存的cookie返回给您。Cookie可以保存您指定的任何的小字符串,这样您就可以使用它来跟踪session。