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

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&gt;&gt;">'

    print "</FORM>"


表单的解析类似GET

form = cgi.FieldStorage()

if form.getfirst('month') == None:

    print_month_quiz()


  • 特殊字符处理

在HTML文档中,字符“<”、“>”和“&”不能被直接插入到文档中。相反,为了显示这些字符,您必须使用“&lt”、“&gt”和“&amp”。有两个方法可以帮助您处理特殊字符: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>&lt;&amp;?+"&gt;test</TT></A><P>      # cgi.escape()


>>> a = '<&?+>'
>>> print cgi.escape(a)
&lt;&amp;?+&gt;
>>> 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。


你可能感兴趣的:(python)