您的位置:首页 > 理论基础 > 计算机网络

[Python网络编程基础]第18章 CGI

2013-04-28 18:09 537 查看
http://book.csdn.net/bookfiles/399/index.html

CGI

CGI,公用网关接口,是一种提供动态网站内容的方法。CGI脚本可以运行在任何支持CGI的Web服务器上,并且CGI脚本是可以由任何语言编写的。CGI既不是一种网络协议,也不是它本身的一种库。而是一种说明信息是如何在Web服务器和产生数据的程序之间交换信息的说明书。

几乎所有流行的Web服务器和编程语言都支持CGI。然而,它也有一些问题,最显著的就是性能。如果需要访问数据库的话,性能会更差。

Python的CGIHTTPServer模块提供了一个运行服务器便捷的方法。

CGI脚本可以访问到客户端表单的信息。接着它会产生一个HTML文档,并在标准输出上输出。Web服务器会确保这个输出被发送给客户端。

CGI 和 Python

下面这些不寻常的情况会对Python程序员有些影响:

- 初始化时间变得至关重要。多数Python程序的初始化效率都不高。本来这通常不是问题,因为这对连续运行3个月的程序来说,不在乎多等几秒钟,但是对于CGI脚本,这是一个非常重要的问题。

- 不同的错误处理。如果一个CGI脚本出现了异常,错误一般会记入Web服务器的日志中,但是客户端会收到错误信息或一个不完整的文档,而不是关于堆栈的追踪。因此错误不会对其他运行的CGI脚本产生影响。

- 交互的处理不同。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

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

### 简单的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

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

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)用来完成这个任务。浏览器提供的文件名就包含在文件名属性中。

使用cookie

CGI的作者经常需要跟踪用户的session。session通常是用户和站点之间连续的交互。

HTTP本身就不是一个常连接的协议,也就是说,用户浏览的每一页都有自己的session,且在不同页之间是没有内置的方法来把它们关联起来的。但是,CGI作者经常需要这个关联。例如:如果您正在开发一个购物站点,您就需要跟踪用户在该站点上的操作,以确保用户什么时候点击了“加入购物车”(Add toCart),以及什么时候用户正在使用购物车。这种请求通常需要某种永久的保存——一般是使用数据库来保存session和购物车信息。

有几种跟踪session信息的方法。例如:您可以使用HTTP认证。如果用户只有登录后才能访问您的站点,而您又使用了HTTP认证,您就可以通过访问REMOTE_USER这个环境变量来得到认证过的用户名,并把它作为一个session记号。
另外一种方法是传递一个session记号。这个记号也许是随机产生的。它在每一个表单中都是一个内嵌的隐藏字段,或者是包含在每一个URL链接的结尾。这样虽然是可以的,但是确实非常麻烦。
现今,很多开发者都喜欢使用cookie机制。Cookie是保存在用户机器上的小记号。当用户访问您站点的时候,浏览器将把上次保存的cookie返回给您。Cookie可以保存您指定的任何的小字符串,这样您就可以使用它来跟踪session。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: