WEB:python CGI 交互式界面实现

7月第3周博客
最近在做一个项目,里面用到了网站的开发,而CGI作为网页开发的基础,要有有一个深入的理解。读了一些好的例子,特别拿出来与大家分享。不过技术的学习也确实是一个循序渐进的过程,很多事情急不得,要慢慢来,大家一起努力。
下面贴出网页交互的全部代码,最后再给大家足部的见解:

#!/usr/bin/env python

from cgi import FieldStorage
from os import environ
from StringIO import StringIO
from urllib import quote, unquote

class AdvCGI(object):
    header = 'Content-Type: text/html\n\n'
    url = '/cgi-bin/advcgi.py'

    formhtml = '''
Advanced CGI Demo

Advanced CGI Demo Form

My Cookie Setting

  • CPPuser = %s

    Enter cookie value
    (optional)

    Enter your name
    (required)

    What languages can you program in? (at least one required)

    %s

    Enter file to upload (max size 4K)

  • '''
    langSet = ('Python', 'Ruby', 'Java', 'C++', 'PHP', 'C', 'JavaScript') langItem = ' %s\n' def getCPPCookies(self): # reads cookies from client if 'HTTP_COOKIE' in environ: cookies = [x.strip() for x in environ['HTTP_COOKIE'].split(';')] for eachCookie in cookies: if len(eachCookie) > 6 and eachCookie[:3] == 'CPP': tag = eachCookie[3:7] try: self.cookies[tag] = eval(unquote(eachCookie[8:])) except (NameError, SyntaxError): self.cookies[tag] = unquote(eachCookie[8:]) if 'info' not in self.cookies: self.cookies['info'] = '' if 'user' not in self.cookies: self.cookies['user'] = '' else: self.cookies['info'] = self.cookies['user'] = '' if self.cookies['info'] != '': self.who, langStr, self.fn = self.cookies['info'].split(':') self.langs = langStr.split(',') else: self.who = self.fn = '' self.langs = ['Python'] def getUserCookie(self): # see if user cookie set up yet if not ('user' in self.cookies and self.cookies['user']): cookStatus = '(cookie has not been set yet)' userCook = '' else: userCook = cookStatus = self.cookies['user'] return userCook def showForm(self): self.getCPPCookies() # put together language checkboxes langStr = [] for eachLang in AdvCGI.langSet: langStr.append(AdvCGI.langItem % (eachLang, ' CHECKED' if eachLang in self.langs else '', eachLang)) userCook = self.getUserCookie() print '%s%s' % (AdvCGI.header, AdvCGI.formhtml % ( AdvCGI.url, cookStatus, userCook, self.who, ''.join(langStr), self.fn)) errhtml = ''' Advanced CGI Demo

    ERROR

    %s

    '''
    def showError(self): print '%s%s' % (AdvCGI.header, AdvCGI.errhtml % (self.error)) reshtml = ''' Advanced CGI Demo

    Your Uploaded Data

    Your cookie value is: %s

    Your name is: %s

    You can program in the following languages:

      %s

    Your uploaded file...
    Name: %s
    Contents:

    %s
    Click here to return to form. '''
    def setCPPCookies(self): for eachCookie in self.cookies: print 'Set-Cookie: CPP%s=%s; path=/' % ( eachCookie, quote(self.cookies[eachCookie])) def doResults(self): MAXBYTES = 4096 langList = ''.join( '
  • %s
    ' % eachLang for eachLang in self.langs) filedata = self.fp.read(MAXBYTES) if len(filedata) == MAXBYTES and f.read(): filedata = '%s%s' % (filedata, '... (file truncated due to size)') self.fp.close() if filedata == '': filedata = '(file not given or upload error)' filename = self.fn userCook = self.getUserCookie() # set cookies self.cookies['info'] = ':'.join( (self.who, ','.join(self.langs), filename)) self.setCPPCookies() print '%s%s' % (AdvCGI.header, AdvCGI.reshtml % ( cookStatus, self.who, langList, filename, filedata, AdvCGI.url)) def go(self): self.cookies = {} self.error = '' form = FieldStorage() if not form.keys(): self.showForm() return if 'person' in form: self.who = form['person'].value.strip().title() if self.who == '': self.error = 'Your name is required. (blank)' else: self.error = 'Your name is required. (missing)' self.cookies['user'] = unquote(form['cookie'].value.strip()) if 'cookie' in form else '' 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.' if 'upfile' in form: upfile = form['upfile'] self.fn = upfile.filename or '' if upfile.file: self.fp = upfile.file else: self.fp = StringIO('(no data)') else: self.fp = StringIO('(no file)') self.fn = '' if not self.error: self.doResults() else: self.showError() if __name__ == '__main__': page = AdvCGI() page.go()
  • 这段代码根据实际测试过的,可用!下面对代码进行简单的分析:
    1、在声明Ad vCGI类之后,创建了header和url变量,在显示不同的页面时,会用到这些变量。下面是HTML静态文本的表单。

    class AdvCGI(object):
        header = 'Content-Type: text/html\n\n'
        url = '/cgi-bin/advcgi.py'
    
        formhtml = '''<HTML><HEAD><TITLE>
    Advanced CGI DemoTITLE>HEAD>
    <BODY><H2>Advanced CGI Demo FormH2>
    <FORM METHOD=post ACTION="%s" ENCTYPE="multipart/form-data">
    <H3>My Cookie SettingH3>
    <LI> <CODE><B>CPPuser = %sB>CODE>
    <H3>Enter cookie value<BR>
    <INPUT NAME=cookie value="%s"> (<I>optionalI>)H3>
    <H3>Enter your name<BR>
    <INPUT NAME=person VALUE="%s"> (<I>requiredI>)H3>
    <H3>What languages can you program in?
    (<I>at least one requiredI>)H3>
    %s
    <H3>Enter file to upload <SMALL>(max size 4K)SMALL>H3>
    <INPUT TYPE=file NAME=upfile VALUE="%s" SIZE=45>
    <P><INPUT TYPE=submit>
    FORM>BODY>HTML>'''

    2、这个例子用到了cookie。下面还有setCPPCookies()方法,应用程序会调用这个方法来发送cookie(从Web 服务器)到浏览器,并存储在浏览器中。getCPPCookies()所做的刚好相反。当浏览器对应用进行连续调用时,这个方法将相同的cookie 通过HTTP 头发送回服务器。在应用执行时,应用可以通过HTTP_COOKIE 环境变量
    访问到这些值。
    这个方法解析cookie,特别是寻找以CPP 开头的字符串。在这个例子中,只查找名值。如果这个cookie 丢失,就会给它指定一个空字符串。getCPPCookies()方法
    只会被showForm()调用。为“CPPuser”和“CPPinfo”的cookie。键“user”和“info”在第38 行提取为标签。跳过了索引7 处的等号。在第39~42 行去除了索引8 处的值并进行计算,计算结果保存到Python对象中。异常处理程序查看cookie 负载,对于非法的Python 对象,仅仅是保存相应的字符串值。如果这个cookie 丢失,就会给它指定一个空字符串(第43~48 行)。getCPPCookies()方法只会被showForm()调用。
    在这个简单的例子中自行解析cookie,但对于复杂的应用,一般使用Cookie 模块(在Python 3 中重命名为http.cookies)来完成这个任务。与之类似,如果在编写Web 客户端,需要管理浏览器存储的所有cookie(一个cookie jar),并与Web 服务器通信,可能会需要用到cookielib 模块(在Python 3 中重命名http.cookiejar)。
    3、showForm()和doReuslts()都会调checkUserCookie()方法,用来检查是否设置了用户提供的cookie 值。表单和结果的HTML 模板都会显示这个值。
    showForm()方法唯一的目的是将表单显示给用户。这个方法需要getCPPCookies()从之前的请求中(如果有)获取cookie,并适当地调整表单的格式。
    4、这一块代码会创建结果页面。setCPPCookies()方法请求客户端为应用程序存储cookie,doResults()方法将所有数据放在一起发送回客户端。go()方法会调用doResults(),用于处理主要任务,以输出数据。在这个方法的第一部分(第109~119 行),用于处理用户数据,即选择的编程语言集(至少需要选择一个,详见go()方法)、上传的文件以及用户提供的cookie 值,后两者都是可选的。doResults()的最后一步(第128~135 行)将所有数据打包到单个“CPPinfo”cookie 中,为后面做准备,并根据数据渲染结果模板。
    6、这段代码首先实例化一个AdvCGI 页面对象,接着调用其中的go()方法开始工作。go()方法用于读取所有将要到达的数据并确定显示哪个页面。如果没有给出名字或选定语言,则会显示错误页面。如果没有收到任何输入数据,将调用showForm()方法来输出表单;否则,将调用doResults()方法来显示结果页面。通过设置self.error 变量可以创建错误页面,这样做有两个目的。一是可以将错误原因设置在字符串里,二是可以作为一个标记表明有错误发生。如果该变量不为空,用户会被导向到错误页面。person 字段是一个键值对,处理方法(第145~150 行)和先前看到的一样。但在收集语言信息时(第153~160 行)需要一点技巧,原因是必须检查一个(Mini)FieldStorage 实例或一个包含该实例的列表。这里将使用熟悉的isinstance()内置函数来达到目的。最终,会获得单个或多个语言名的列表,具体依赖于用户的选择情况。
    如果使用cookie 来保管数据,就可以避免使用任何类型的CGI 字段。在本章之前的示例中,将这些值作为CGI 变量传递。而现在只使用cookie。读者会注意到获取这些数据的代码没有调用CGI 处理,这意味着数据并非来自FieldStorage 对象。Web 客户端的每次请求都会发送相应的数据,其中的值从cookie 中获得(包括用户的选择结果和用来填充后续表单的已有信息)。
    因为showResults()方法从用户那里取得了新的输入值,所以该方法负责设置cookie,例如,通过调用setCPPCookies()。而showForm()必须读出cookie 中的值才能用表单页显示用户的当前选项。这通过getCPPCookies()的调用来实现。
    最后,来看看文件的上传处理(第162~171 行)。不论一个文件实际上是否已经上传,FieldStorage 都会从file 属性中获得一个文件句柄。在第171 行,如果没有指明文件名,就把它设置成空字符串。还有一个更好的做法,可以访问文件指针(即file 属性),并且可以每次只读一行或者其他更慢一些的处理方法。
    在这个例子里,文件上传只是用户提交过程的一部分,所以只是简单地把文件指针传给doResults()函数,从文件中抽取数据。由于受到空间限制,doResults()将只显示文件的前 4KB内容(第112 行),完全没有必要显示一个4GB 的完整二进制文件。

    你可能感兴趣的:(web服务器开发,python)