前端代码是可以重用的。Tornado通过extends和block语句支持模板继承。
{% extends “main.html” %}而block压缩了以后要扩展的模板元素。
示例如下:
main.py:
# -*- coding:utf-8 -*- import os.path import random import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) #主文件:重定向到index.html class IndexHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", header_text = "Header goes here", footer_text = "Footer goes here") if __name__ == '__main__': tornado.options.parse_command_line() app = tornado.web.Application( handlers = [(r'/', IndexHandler) ], #html文件放在templates中 template_path = os.path.join(os.path.dirname(__file__), 'templates'), #css文件放在static中 static_path = os.path.join(os.path.dirname(__file__), 'static'), debug = True ) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()index.html:
{% extends "main.html" %} {% block header %} <h1>{{ header_text }}</h1> {% end %} {% block body %} <p>Hello from the child template!</p> {% end %} {% block footer %} <p>{{ footer_text }}</p> {% end %}main.html:
<html> <body> <head> {% block header %}{% end %} </head> <content> {% block body %}{% end %} </content> <footer> {% block footer %}{% end %} </footer> </body> </html>浏览器显示如下:
main.py:
# -*- coding:utf-8 -*- import os.path import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class Application(tornado.web.Application): def __init__(self): #处理类 handlers = [ (r"/", MainHandler), ] #配置(路径等) settings = dict( template_path = os.path.join(os.path.dirname(__file__), "templates"), static_path = os.path.join(os.path.dirname(__file__), "static"), debug = True, ) #初始化Appliction类 tornado.web.Application.__init__(self, handlers, **settings) class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", page_title = "Burt's Books | Home", header_text = "Welcome to Burt's Books!", ) if __name__ == '__main__': tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()index.html:
{% extends "main.html" %} {% block header %} <h1>{{ header_text }}</h1> {% end %} {% block body %} <div id="hello"> <p>Welcome to Burt's Books!</p> <p>...</p> </div> {% end %}main.html:
<html> <head> <title>{{ page_title }}</title> <link rel="stylesheet" type="text/css" href="{{ static_url("css/style.css") }}"> </head> <body> <div id="container"> <header> {% block header %}<h1>Burt's Books</h1>{% end %} </header> <div id="main"> {% block body %}{% end %} </div> </div> <footer> {% block footer %} <p> For more information about our selection, hours or events, please email us at <a href="mailto:[email protected]">[email protected]</a> </p> {% end %} </footer> <div> <script src="{{ static_url("js/script.js")}}"></script> </div> </body> </html>备注:
1. 将style.css文件放入static/css文件夹下
2. js/script.js文件并未编写(用于下面的测试)
运行程序:
python main.py --port=8000
浏览器显示如下:
现在假设js/script.js被黑客所改写,他编写了一段代码会弹窗(实际应用中它会死循环,一直弹窗,或者后台执行.......):
alert("I am a hacker!")则我们访问网站时候会出现这种情况:
这种情况下我们是无能为力了(毕竟源代码都被修改了)。但是假设我们有一个评论区,而用户直接编写:
<script type="text/javascript">alert("I am a hacker!")</script>那是否会直接弹窗呢?
3. 自动转义
Tornado会自动转义在双大括号间被渲染的表达式。测试用例如下:
main.py:
# -*- coding:utf-8 -*- import os.path import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class Application(tornado.web.Application): def __init__(self): #处理类 handlers = [ (r"/", MainHandler), ] #配置(路径等) settings = dict( template_path = os.path.join(os.path.dirname(__file__), "templates"), static_path = os.path.join(os.path.dirname(__file__), "static"), debug = True, ) #初始化Appliction类 tornado.web.Application.__init__(self, handlers, **settings) class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", page_title = "Burt's Books | Home", header_text = "Welcome to Burt's Books!", #编写脚本,用于测试是否被转义 comments = '<script type="text/javascript">alert("I am a hacker!")</script>' ) if __name__ == '__main__': tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()index.html:
{% extends "main.html" %} {% block header %} <h1>{{ header_text }}</h1> <h2>Burt's Books:h2</h2> {% end %}main.html:
<html> <head> <title>{{ page_title }}</title> <link rel="stylesheet" type="text/css" href="{{ static_url("css/style.css") }}"> </head> <body> <div id="container"> <header> <!--这里<h3></h3>之所以不起作用,是因为它会被index.html中的{% block header %}整体替换掉--> {% block header %}<h3>Burt's Books:h3</h3>{% end %} </header> <div id="main"> {{ comments }} </div> </div> </body> </html>浏览器显示如下:
而我们通过查看源代码可以发现script脚本已经被转义了:
<div id="main"> <script type="text/javascript">alert("I am a hacker!")</script> </div>
1) 可以在Application构造函数中传递autoescape=None:
2) 可以直接编写{% autoescape None %}
测试示例如下:
main.py:
# -*- coding:utf-8 -*- import os.path import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class Application(tornado.web.Application): #为什么这里autoescape不起作用? def __init__(self, autoescape = None): #处理类 handlers = [ (r"/", MainHandler), ] #配置(路径等) settings = dict( template_path = os.path.join(os.path.dirname(__file__), "templates"), static_path = os.path.join(os.path.dirname(__file__), "static"), debug = True, autoescape = None, ) #初始化Appliction类 tornado.web.Application.__init__(self, handlers, **settings) class MainHandler(tornado.web.RequestHandler): def get(self): self.render("index.html", page_title = "Burt's Books | Home", header_text = "Welcome to Burt's Books!", #编写脚本,用于测试是否被转义 comments = '<script type="text/javascript">alert("I am a hacker!")</script>' ) if __name__ == '__main__': tornado.options.parse_command_line() http_server = tornado.httpserver.HTTPServer(Application(None)) http_server.listen(options.port) tornado.ioloop.IOLoop.instance().start()index.html:
{% extends "main.html" %} {% block header %} <h1>{{ header_text }}</h1> {% end %}main.html:
<html> <head> <title>{{ page_title }}</title> <link rel="stylesheet" type="text/css" href="{{ static_url("css/style.css") }}"> </head> <body> <div id="container"> <header> {% block header %}{% end %} </header> <div id="main"> <!-- 测试Application的初始化函数中autoescape = None是否起作用 --> {{ comments }} <!-- 使用autoescape None输出转义内容,而autoescape xhtml_escape开启自动转义 --> {% autoescape None %} {% autoescape xhtml_escape %} {{ comments }} <!-- 使用raw指令来输出不转义的内容 --> {% raw comments %} </div> </div> </body> </html>浏览器显示如下:
这里autoescape = None并不起作用,这到底是为什么?
UI模块是封装模板中包含的标记,样式以及行为的可复用组件。它所定义的元素通常用于多个模板交叉复用或在同一个模板中重复使用。模板本身是一个继承自Tornado的UIModule类的简单Python类,并定义了一个render方法。当一个模板使用{% module Foo(...) %}标签引用一个模块时,Tornado的模板引擎调用模块的render方法,然后返回一个字符串来代替模板中的模块标签。UI模块也可以在渲染后的页面中嵌入自己的JavaScript或CSS文件。我们可以定义可选的embedded_javascript,embedded_css,javascript_files和css_files方法来实现这个方法。
hello_module.py:
# -*- coding:utf-8 -*- import tornado.web import tornado.httpserver import tornado.ioloop import tornado.options import os.path from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) class HelloHandler(tornado.web.RequestHandler): def get(self): self.render('hello.html') #定义UI模块 class HelloModule(tornado.web.UIModule): def render(self): return '<h1>Hello, world!</h1>' class Application(tornado.web.Application): def __init__(self): #处理类 handlers = [(r'/', HelloHandler)] #配置 settings = dict( template_path = os.path.join(os.path.dirname(__file__), 'templates'), ui_modules = {'Hello' : HelloModule} ) tornado.web.Application.__init__(self, handlers, **settings) if __name__ == '__main__': tornado.options.parse_command_line() server = tornado.httpserver.HTTPServer(Application()) server.listen(options.port) tornado.ioloop.IOLoop.instance().start()hello.html:
<html> <head><title>UI Module Example</title></head> <body> <!-- 调用模块Hello() --> {% module Hello() %} </body> </html>浏览器输出如下:
一个非常有用的做法是让模块指向一个iemuban文件而不是在模块类中直接渲染字符串。
示例:显示每本书的具体信息,模块为书的信息
recommended.py:
# -*- coding:utf-8 -*- import tornado.web import tornado.httpserver import tornado.ioloop import tornado.options import os.path from tornado.options import define, options define("port", default=8000, help="run on the given port", type=int) #UI模块:向module/book.html模块中传递book class BookModule(tornado.web.UIModule): def render(self, book): return self.render_string('modules/book.html', book=book) class IndexHandler(tornado.web.RequestHandler): def get(self): books = [ {'title' : 'book 1', 'contents' : 'contents 1'}, {'title' : 'book 2', 'contents' : 'contents 2'}, {'title' : 'book 3', 'contents' : 'contents 3'} ] self.render('recommended.html', books=books) class Application(tornado.web.Application): def __init__(self): handlers = [ (r'/', IndexHandler), ] #配置 setting = dict( template_path = os.path.join(os.path.dirname(__file__), 'templates'), static_path = os.path.join(os.path.dirname(__file__), "static"), debug = True, ui_modules = {'Book' : BookModule} ) tornado.web.Application.__init__(self, handlers, **setting) if __name__ == '__main__': tornado.options.parse_command_line() server = tornado.httpserver.HTTPServer(Application()) server.listen(options.port) tornado.ioloop.IOLoop.instance().start()recommended.html:
{% extends "main.html" %} {% block body %} <h2>Recommended Reading</h2> {% for book in books %} {% module Book(book) %} {% end %} {% end %}book.html:
<div class='book'> <h3 class='book_title'>{{ book['title'] }}</h3> <p>{{ book['contents'] }}</p> </div>main.html:
<html> <head> <title>Books</title> <link rel="stylesheet" type="text/css" href="{{ static_url("css/style.css") }}"> </head> <body> <div class='book'> {% block body %}{% end %} </div> </body> </html>浏览器显示如下: