白话 Tornado 源码(5):褪去模板的外衣
上一篇《白话tornado源码(3):请求来了》介绍了客户端请求在tornado框架中的生命周期,其本质就是利用epoll和socket来获取并处理请求。在上一篇的内容中,我们只是给客户端返回了简单的字符串,如:“Hello World”,而在实际开发中,需要使用html文件的内容作为模板,然后将被处理后的数据(计算或数据库中的数据)嵌套在模板中,然后将嵌套了数据的html文件的内容返回给请求者客户端,本篇就来详细的剖析模板处理的整个过程。
概述
(配图超大,请点击这里看大图)
上图是返回给用户一个html文件的整个流程,较之前的Demo多了绿色流线的步骤,其实就是把【self.write(‘hello world’)】变成了【self.render(‘main.html’)】,对于所有的绿色流线只做了五件事:
- 使用内置的open函数读取Html文件中的内容
- 根据模板语言的标签分割Html文件的内容,例如:{{}} 或 {%%}
- 将分割后的部分数据块格式化成特殊的字符串(表达式)
- 通过python的内置函数执行字符串表达式,即:将html文件的内容和嵌套的数据整合
- 将数据返回给请求客户端
所以,如果要返回给客户端对于一个html文件来说,根据上述的5个阶段其内容的变化过程应该是这样:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render(“main.html”,**{‘data’:[’11’,’22’,’33’],‘title’:‘main’})
[main.html]
<!DOCTYPE html>
<html>
<head lang=“en”>
<meta charset=“UTF-8”>
<title></title>
</head>
<body>
<h1>{{title}}</h1>
{% for item in data %}
<h3>{{item}}</h3>
{% end %}
</body>
</html>
XXXHandler.get
|
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!DOCTYPE html>
<html>
<head lang=“en”>
<meta charset=“UTF-8”>
<title></title>
</head>
<body>
<h1>{{title}}</h1>
{% for item in data %}
<h3>{{item}}</h3>
{% end %}
</body>
</html>
|
Python
1
2
3
4
5
6
7
8
|
第1块:‘
‘第2块:‘title’
第3块:‘
nn’ 第4块:‘for item in data’
第4.1块:‘n
‘ 第4.2块:‘item’
第4.3块:‘
n’ 第五块:‘‘
|
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
‘def _execute():
_buffer = []
_buffer.append(\’<!DOCTYPE html>n<html>n<head lang=“en”>n<meta charset=“UTF-8”>n<title></title>n</head>n<body>n<h1>‘)
_tmp = title
if isinstance(_tmp, str): _buffer.append(_tmp)
elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode(\’utf–8‘))
else: _buffer.append(str(_tmp))
_buffer.append(\’</h1>n‘)
for item in data:
_buffer.append(\’n<h3>‘)
_tmp = item
if isinstance(_tmp, str): _buffer.append(_tmp)
elif isinstance(_tmp, unicode): _buffer.append(_tmp.encode(\’utf–8‘))
else: _buffer.append(str(_tmp))
_buffer.append(\’</h3>n‘)
_buffer.append(\’n</body>n</html>‘)
return \’‘.join(_buffer)
‘
|
1
2
|
a、参照本篇博文的前戏 http://www.cnblogs.com/wupeiqi/p/4592637.html
b、全局变量有 title = ‘main’;data = [’11’,’22’,’33’]
|
在第4步中,执行第3步生成的字符串表示的函数后得到的返回值就是要返回给客户端的响应信息主要内容。
3.13、RequestHandler的render方法
此段代码主要有三项任务:
- 获取Html文件内容并把数据(程序数据或框架自带数据)嵌套在内容中的指定标签中(本篇主题)
- 执行ui_modules,再次在html中插入内容,例:head,js文件、js内容、css文件、css内容和body
- 内部调用客户端socket,将处理请求后的数据返回给请求客户端
Python
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
class RequestHandler(object):
def render(self, template_name, **kwargs):
#根据Html文件名称获取文件内容并把参数kwargs嵌入到内容的指定标签内
html = self.render_string(template_name, **kwargs)
#执行ui_modules,再在html的内容中插入head,js文件、js内容、css文件、css内容和body信息。
js_embed = []
js_files = []
css_embed = []
css_files = []
html_heads = []
html_bodies = []
for module in getattr(self, “_active_modules”, {}).itervalues():
embed_part = module.embedded_javascript()
if embed_part: js_embed.append(_utf8(embed_part))
file_part = module.javascript_files()
if file_part:
if isinstance(file_part, basestring):
js_files.append(file_part)
else:
js_files.extend(file_part)
embed_part = module.embedded_css()
if embed_part: css_embed.append(_utf8(embed_part))
file_part = module.css_files()
if file_part:
if isinstance(file_part, basestring):
css_files.append(file_part)
else:
css_files.extend(file_part)
head_part = module.html_head()
if head_part: html_heads.append(_utf8(head_part))
body_part = module.html_body()
if body_part: html_bodies.append(_utf8(body_part))
if js_files:#添加js文件
# Maintain order of JavaScript files given by modules
paths = []
unique_paths = set()
for path in js_files:
if not path.startswith(“/”) and not path.startswith(“http:”):
path = self.static_url(path)
if path not in unique_paths:
paths.append(path)
unique_paths.add(path)
js = ”.join(‘‘
for p in paths)
sloc = html.rindex(‘ |