使用 Twisted 框架进行网络编程--有状态 Web 服务器和模板化(2)
- UID
- 1066743
|
使用 Twisted 框架进行网络编程--有状态 Web 服务器和模板化(2)
利用 Woven 进行模板化我们到目前为止所介绍的动态页面在概念上都类似于 CGI 方法。Twisted 异步服务器比较快——它尤其节省时间,从而避免了为每个脚本请求打开一个新进程所带来的开销。但是 fastcgi 或 mod_python 获得一个类似的加速。Twisted 在这一方面没有什么特殊的。
将 Web 应用开发上升到一个较高水平的方法之一就是使用 Woven。从概念上讲,Woven 有些类似于 PHP、ASP (ActiveServer Pages)或 JSP (JavaServer Pages)。也就是说,Woven XHTML 页面不仅仅是向浏览器传递页面,而且传递以编程方式填充的页面的模板或骨架。但是,对于代码和HTML 之间的分离,利用 Woven 比利用这些页面嵌入技术要稍微复杂一些。您不是将 Python 代码直接写入到 Woven 模板中,而是在一些普通的标记上定义一系列的自定义XHTML 属性,从而使外部代码增强并处理页面,然后再向浏览器客户端传递页面。
model 属性确定将用于扩展 XHTML 元素的数据。思路是,Model 表示应用程序的“业务逻辑”,即页面的数据内容是如何确定的。而 view 属性则确定所生成数据的特定表示。在 Woven 中还有 Controller 的概念,它是将节点(也就是XHTML 元素)的 Model 和 View组合在一起的代码。这一最后部分通常由一个 Page 对象来处理,该对象是一个可以被特殊化的类。
诚然,Woven 的术语有些难于理解,并且不幸的是,Twisted Matrix Web 站点的 HOWTO 文档在阐述这些术语的时候,几乎也是混淆使用。很难确切地阐述如何使用Woven。我并不宣称我自己完全理解 Woven 概念,但是 Twisted 的用户 Alex Levy (请参见 ,获得他的页面链接)帮助我开发了下面给出的例子。但是,您仍然可以利用 Woven 来做很多事情,所以是值得学习的。
开发 Woven 应用程序的第一步是建立一个或多个模板文件。这些模板文件就是具有特殊属性的 XHTML 文件,例如:
清单 5. WeblogViewer.xhtml 模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| <html>
<head>
<title>Weblog Viewer</title>
<meta HTTP-EQUIV="Refresh" CONTENT="30" />
<style type="text/css"><!--
div.info {
background-color: lightblue;
padding: 2px dotted; }
table th, table td {
text-align: left;
cellspacing: 0px;
cellpadding: 0px; }
table.log {
border: 0px;
width: 100%; }
table.log tr.even { background-color: white; }
table.log tr.odd { background-color: lightgray; }
--></style>
</head>
<body>
<div class="info">
You are displaying the contents of
<code model="filename" view="Text">filename</code>.
</div>
<table border="0" cellspacing="0" width="100%"
class="log" model="entries" view="List">
<tr bgcolor="yellow" pattern="listHeader">
<th>Referrer</th><th/>
<th>Resource</th>
</tr>
<tr pattern="listItem" view="alternateColor">
<td model="referrer" view="Text">
Referrer</td>
<td>-></td>
<td model="request_resource" view="Text">
Resource</td>
</tr>
<tr pattern="emptyList">
<td colspan="2">There is nothing to display.</td>
</tr>
</table>
</body>
</html>
|
Alex Levy 开发了这个模板,并使用 CSS2 来控制元素的确切表示,显示出的样式比我的例子中的更好。很明显,不管有没有样式表,页面的基本布局都是相同的。
注意,分配给 <table> 元素的 View 是“List”,这个 View 与“Text”一样是基本的Woven View。另一方面, “alternateColor”是一个自定义的 View,我们定义在下面的代码中。有些元素具有一个 pattern 属性,控制 View 使用该属性来定位匹配的孩子。特别地,一个 List View 由一个可选的 listHeader 、一些 listItem 孩子(一个模板标记,但是在生成时会扩展)和一个 emptyList 孩子(以免Model 未定位到任何数据)组成。这些模式是 List View所使用的标准属性;其他 Views 将利用其他模式来进行扩展。
这一版本的 weblog 服务器的代码创建了一个自定义的 Twisted 服务器。不是基于客户端的请求来更新,我们向服务器的 Reactor添加了一个对 update() 函数的重复回叫;这与本系列前一部分中的 tlogmaker.py 完全一致。在开始研究自定义的 Page 资源之前,我们先来看看设置代码:
清单 6. WeblogViewer.py 自定义 Twisted 服务器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| import webloglib as wll
import os, sys
from urllib import unquote_plus as uqp
from twisted.internet import reactor
from twisted.web import microdom
from twisted.web.woven import page, widgets
logfile = '../access-log'
LOG = open(logfile)
RECS = []
NUM_ROWS = 25
def update():
global RECS
RECS.extend(LOG.readlines())
RECS = RECS[-NUM_ROWS*3:]
reactor.callLater(5, update)
update()
|
有趣的东西在于我们对类 twisted.web.woven.page.Page 的自定义。我们所做的大部分事情都是不可思议的,因为您需要定义特别指定的属性和方法。
清单 7. WeblogViewer.py Twisted服务器 (续)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| class WeblogViewer(page.Page):
"""A Page used for viewing Apache access logs."""
templateDirectory = '~/twisted/www'
templateFile = "WeblogViewer.xhtml"
# View factories and updates
def wvupdate_alternateColor(self, request, node, data):
"""Makes our table rows alternate CSS classes"""
# microdom.lmx is very handy; another example is located here:
# http://twistedmatrix.com/documents/howto/picturepile#auto0
tr = microdom.lmx(node)
tr['class'] = ('odd','even')[data['_number']%2]
# Model factories
def wmfactory_filename(self, request):
"""Returns the filename of the log being examined."""
return os.path.split(logfile)[1]
def wmfactory_entries(self, request):
"""Return list of dict objects representing log entries"""
entries = []
for rec in RECS:
hit = [field.strip('"') for field in wll.log_fields(rec)]
if hit[wll.status] == '200' and hit[wll.referrer] != '-':
# We add _number so our alternateColor view will work.
d = {'_number': len(entries),
'ip': hit[wll.ip],
'timestamp': hit[wll.timestamp],
'request': hit[wll.request],
'request_resource': hit[wll.request].split()[1],
'status': hit[wll.status],
'bytes': hit[wll.bytes],
'referrer': uqp(hit[wll.referrer]).\
replace('&&',' &'),
'agent': hit[wll.agent],
}
entries.append(d)
return entries[-NUM_ROWS:]
resource = WeblogViewer()
|
我们的自定义 Page 做了三类事情。第一类是设置模板,以便与该资源一起使用。
第二类是使用前缀为 wv (Woven view)的神奇方法来定义一个自定义的 View。我们在自定义的 View中真正所做的全部事情是将 class 属性设置为 CSS 样式表中的两个值中的一个,以使交错的行显示不同的颜色。但是您可以使用一个类似于DOM 的 API 来根据自己的喜好处理代码。
第三类事情是有趣的。通过在 Model 本身的名称前面加上 wmfactory_ 前缀,我们定义了两个 Model。因为 filename 以 Text View 显示,所以最好是返回一个字符串。同样, entries 以 List View 显示,所以应该将一列项作为返回值。但是,XHTML 模板中使用的 referrer 和 request_resource 这两个 Model该如何呢?不用为这两个模型定义自定义的方法。但是, 围绕利用这些Model的节点的 listItem 模式有一个可用的词典—— entries 词典由 .wmfactory_entries() 返回。而该词典又包含 request_resource 和 referrer 的关键字;您不需要一个自定义的方法来支持Model,只要一个带有必需关键字的词典就行了。因为 referrer 节点的 View 是 Text,所以说词典包含的值应该是字符串(如果不是这样,Woven将进行强制转换)。
基于自定义的 WeblogViewer.py 资源创建一个自定义的服务器与我们以前讨论过的一样。创建一个服务器,然后再启动它:
% mktap web --resource-script=WeblogViewer.py --port 8080
% twistd -f web.tap 在最后一部分中这篇介绍只涉及了 Woven 的一些皮毛。该软件包中还有许多复杂的功能,我希望自己给出的例子能对模板化系统起到抛砖引玉的作用。
下一次,在关于 Twisted 这一系列的最后一部分中,我将介绍一些零碎的东西,包括对安全性的一个简要概述。我们还将介绍 Twisted软件包中包含的一些特殊协议和服务器。 |
|
|
|
|
|