使用 Twisted 框架进行网络编程--有状态 Web 服务器和模板化(1)
- UID
- 1066743
|
使用 Twisted 框架进行网络编程--有状态 Web 服务器和模板化(1)
与 Web 浏览器交互在本系列的 中,我介绍了 Twisted 使用 .rpy 扩展名提供的动态 Web 页面。但是 weblog 服务器的这些初始版本只能提供最低限度的动态。我使用了HTML 标记来强迫页面周期性地刷新,并且每执行一次刷新,都要进行一些计算,以确定相应的最近更新。但是没有提到服务器的用户配置方面。
因此,本文将要介绍的第一件事就是,如何在我们上次看到过的同一基本动态页面框架中配置用户交互。但是在开始之前,我将为那些没有阅读本系列前两个部分的读者快速回顾一下如何启动Twisted Web 服务器。
创建一个 “精简的应用程序” 通常是最好的方法,并且这完全可以利用命令行选项来完成。但不是说 必须这样做。只要您愿意,也可以在基本的Web 服务器中包含一些额外的功能(比如跨用户和会话维护持久性数据),却不必编写任何自定义代码。创建精简的应用程序的方法类似于:
mktap web --path ~/twisted/www --port 8080 利用下面的命令启动该应用程序:
twistd -f web.tap 就是这样的。碰巧在 ~/twisted/www 基本目录(或子目录)中的任何 HTML 或 .rpy 文件将为端口 8080 上的客户端服务。实际上,您可以提供任何您喜欢的文件类型,只是.rpy 文件将被看作是特殊的动态脚本。
动态页面 config_refresher.rpy比本系列前一部分给出的任何页面要稍微长一些,因为它在主体中包含了 HTML 模板而不是导入模板。我们首先来看设置代码:
清单 1. 动态脚本config_refresher.py (设置)
1
2
3
4
5
6
7
| from twisted.web import resource, server
from persist import Records
from webloglib import log_fields, COLOR
from urllib import unquote_plus as uqp
fieldnames = """ip timestamp request status
bytes referrer agent""".split()
field_dict = dict(zip(fieldnames, range(len(fieldnames))))
|
与我们在前面两个部分所看到的一些导入不同,我将字段名称映射到它们在 log_fields() 返回的元组中的位置。还请注意自定义 persist 模块的使用,该模块将在 Twisted Web 服务器的内存中保存 weblog,所以不必在每次客户端请求记录时都读取整个日记文件。接下来介绍 HTML 模板:
清单 2. config_refresher.py 脚本 (模板)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| TOP = '''<html><head><title>Weblog Refresher</title>
<META HTTP-EQUIV="Refresh" CONTENT="30"/></head>
<body>
<table border="1" width="100%%">
<tr bgcolor="yellow">
<form action="http://gnosis.cx:8080/config_refresher.rpy"
method="GET">
<td> IP <input type="checkbox" name="ip" %s/> </td>
<td> Timestamp <input type="checkbox" name="timestamp" %s/></td>
<td> Request <input type="checkbox" name="request" %s/></td>
<td> Status <input type="checkbox" name="status" %s/></td>
<td> Bytes <input type="checkbox" name="bytes" %s/></td>
<td> Referrer <input type="checkbox" name="referrer" %s/></td>
<td> Agent <input type="checkbox" name="agent" %s/></td>
<td> <input type="submit" value="Change Fields"></td>
</form>
</td></tr>
<table border="0" cellspacing="0" width="100%%">'''
ROW = '<tr bgcolor=" %s">%s</tr>\n'
END = '</table></body></html>'
COLOR = ['white','lightgray']
END = '''</table></body></html>'''
|
设置 HTML 表单并不太神秘,本例的一个技巧是在 HTML中将那些已经检查过的复选框中添加上字符串“checked”。
清单 3. config_refresher.py脚本 (持久性)
1
2
3
4
| records = registry.getComponent(Records)
if not records:
records = Records()
registry.setComponent(Records, records)
|
Twisted 注册表像本系列前一部分描述的那样工作。它就是保存 Web 日记文件中最新记录的地方。最后,我们创建一个 Resource ,带有一个相应的 .render() 方法——它完成真正的页面创建:
清单 4. config_refresher.py 脚本 (呈现)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| class Resource(resource.Resource):
def render(self, request):
showlist = []
for field in request.args.keys():
showlist.append(field_dict[field])
showlist.sort()
checked = [""] * len(fieldnames)
for n in showlist:
checked[n] = 'checked'
request.write(TOP % tuple(checked))
odd = 0
for rec in records.getNew():
hit = [field.strip('"') for field in log_fields(rec)]
flds='\n'.join(['<td>%s</td>'%hit[n] for n in showlist])
request.write(ROW % (COLOR[odd],
uqp(flds).replace('&&',' &')))
odd = not odd
request.write(END)
request.finish()
return server.NOT_DONE_YET
resource = Resource()
|
该 Resource 中主要的新东西是对 request.args 属性的访问。一般来说,该属性类似于 cgi 模块中的 FieldStorage 类——它收集与页面请求一起传递的任何信息,既包括GET 数据,也包括 PUT 数据。Twisted的请求数据是所传递值的词典;在我们的例子中,我们只关心传递进来了哪些复选框的字段,以及未传递进哪些字段。如果我们想要检查保存在 request.args 中的一些值,则将遵循相同的模式。例如,您可能基于字段值将选项添加到过滤器(并选择该过滤器带有一个文本项或者一个HTML 列表框)。 |
|
|
|
|
|