WEB:python CGI 交互式界面实现

来源:互联网 发布:计算机编程与维护 编辑:程序博客网 时间:2024/06/17 04:26

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

#!/usr/bin/env pythonfrom cgi import FieldStoragefrom os import environfrom StringIO import StringIOfrom urllib import quote, unquoteclass AdvCGI(object):    header = 'Content-Type: text/html\n\n'    url = '/cgi-bin/advcgi.py'    formhtml = '''<HTML><HEAD><TITLE>Advanced CGI Demo</TITLE></HEAD><BODY><H2>Advanced CGI Demo Form</H2><FORM METHOD=post ACTION="%s" ENCTYPE="multipart/form-data"><H3>My Cookie Setting</H3><LI> <CODE><B>CPPuser = %s</B></CODE><H3>Enter cookie value<BR><INPUT NAME=cookie value="%s"> (<I>optional</I>)</H3><H3>Enter your name<BR><INPUT NAME=person VALUE="%s"> (<I>required</I>)</H3><H3>What languages can you program in?(<I>at least one required</I>)</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>'''    langSet = ('Python', 'Ruby', 'Java', 'C++', 'PHP', 'C', 'JavaScript')    langItem = '<INPUT TYPE=checkbox NAME=lang VALUE="%s"%s> %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 = '<I>(cookie has not been set yet)</I>'            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 = '''<HTML><HEAD><TITLE>Advanced CGI Demo</TITLE></HEAD><BODY><H3>ERROR</H3><B>%s</B><P><FORM><INPUT TYPE=button VALUE=BackONCLICK="window.history.back()"></FORM></BODY></HTML>'''    def showError(self):        print '%s%s' % (AdvCGI.header, AdvCGI.errhtml % (self.error))    reshtml = '''<HTML><HEAD><TITLE>Advanced CGI Demo</TITLE></HEAD><BODY><H2>Your Uploaded Data</H2><H3>Your cookie value is: <B>%s</B></H3><H3>Your name is: <B>%s</B></H3><H3>You can program in the following languages:</H3><UL>%s</UL><H3>Your uploaded file...<BR>Name: <I>%s</I><BR>Contents:</H3><PRE>%s</PRE>Click <A HREF="%s"><B>here</B></A> to return to form.</BODY></HTML>'''    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(            '<LI>%s<BR>' % eachLang for eachLang in self.langs)        filedata = self.fp.read(MAXBYTES)        if len(filedata) == MAXBYTES and f.read():            filedata = '%s%s' % (filedata,              '... <B><I>(file truncated due to size)</I></B>')        self.fp.close()        if filedata == '':            filedata = '<B><I>(file not given or upload error)</I></B>'        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 Demo</TITLE></HEAD><BODY><H2>Advanced CGI Demo Form</H2><FORM METHOD=post ACTION="%s" ENCTYPE="multipart/form-data"><H3>My Cookie Setting</H3><LI> <CODE><B>CPPuser = %s</B></CODE><H3>Enter cookie value<BR><INPUT NAME=cookie value="%s"> (<I>optional</I>)</H3><H3>Enter your name<BR><INPUT NAME=person VALUE="%s"> (<I>required</I>)</H3><H3>What languages can you program in?(<I>at least one required</I>)</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 的完整二进制文件。

原创粉丝点击