模板
走个流程
配置模板路径
1
2
3
4settings = {
'static_path':os.path.join(BASE_DIRS,'static'),
'template_path':os.path.join(BASE_DIRS,'templates'),
}模板为
1
2
3
4
5<head>
<link rel="stylesheet" type="text/css" href="{{ static_url('css/style.css') }}">
</head>
<li>{{ num }}</li>使用
self.render
1
2
3
4
5
6class TestHandler5(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
context = {
'num':55,
}
self.render('index2.html',**context)
变量与表达式
变量 :
{{ var }}
tornado模板系统更为强大 , 支持表达式
{{ expression }}
{{ num +10 }}
{{ num==200 }}
这就造成了在tornado中,不能使用Django中的点号语法:
1
2
3
4
5
6
7class TestHandler5(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
context = {
'num':55,
'd':{'name':'hyl'},
}
self.render('index2.html',**context)1
2
3<a>{{ d }}</a>
{# 使用字典的索引 #}
<a>{{ d['name'] }}</a>在jango中是
1
2
3<a>{{ d }}</a>
{# 使用点号索引 #}
<a>{{ d.name }}</a>
流程控制
if
1
2
3
4
5{{% if 表达式 %}}
语句
{{% elif 表达式 %}}
语句
{{% end %}}Django是
{{% endif %}}
for
1
2
3{{% for 变量 in 集合 %}}
语句
{{% end %}}Django是
{{% endfor %}}
while
函数
static_url()
1 | settings = { |
1 | <head> |
- static_url创建了一个基于文件内容的hash值,并将其添加到URL末尾当一个查词参数
- 这个hash值总能保证加载的都是最新的文件,而不是以前的缓存版本,
- 不论是开发阶段还是上线阶段都是很有必要的
自定义函数
在Handler里面定义函数 , 然后将其传给模板
1 | class TestHandler5(tornado.web.RequestHandler): |
1 | <li>{{ mysum(100,2) }}</li> |
转义
tornado默认开启了自动转义功能,能防止网站受到恶意攻击
关闭转义:
raw标签 : 单次关闭
{{% raw data %}}
1
2
3
4
5
6class TestHandler8(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
context = {
'data':'<a>a标签</a>'
}
return self.render('index2.html',**context)1
<li>{% raw data %}</li>
autoescape标签 : 关闭当前模板
{% autoescape None %}
在配置中设置: 关闭全部模板
1
2
3settings = {
'autoescape' : None
}escape()函数 : 在关闭自动转义后,可以使用该方法刘特定的变量进行转义
1
2
3
4
5{% autoescape None %}
{{ data }}
<!-- 转义 -->
{{ escape(data) }}
{{ data }}
继承
1 | <!-- base.html --> |
1 | {% extends 'base.html' %} |
静态文件
static_url()
作用 : 告诉 tornado从文件系统中的某一个特定的位置提供静态文件 :
'static_path':os.path.join(BASE_DIRS,'static'),
一般我们的static文件下的子文件夹为
css
,js
,img
. 有可能还有html
页面 . 也就是说,我们可以直接访问这个html页面资源- 访问
www.baidu.com/index.html
返回百度首页 , 此时并不是使用url的重定向 , 而是直接访问index.html
这个文件 - 就像我们使用
{% static css/style.css %}
一样,获取{% static html/index.html %}
- 一般这种html都是
静态
的 , 意思就是这个html不含有{{ var }}
,{% if... %}
等变量或标签
也就是说说 ,当前的staic文件夹如下:
1
2
3
4
5
6static
├─js
├─img
├─css
└─html
└─index.html通过
http://127.0.0.1:8000/static/html/index.html
来直接访问index.html这个页面- 访问
但是
http://127.0.0.1:8000/static/html/index.html
层次过深 , 我们希望改为http://127.0.0.1:8000/index.html
. 此时就需要使用到StaticFileHandler
对象
StaticFileHandler对象
使用原因 :
http://127.0.0.1:8000/static/html/index.html
对于用户来说体验不佳本质 : tornado预制的用来提供静态资文件的 handler
作用 : 通过
tornado.web.StaticFileHandler
来映射静态文件使用 :
只要在路由里添加:1
2
3
4# 注意在最后添加此路由
# path为文件查找的路径
# default_filename为默认寻找的文件
(r'/(.*)$',tornado.web.StaticFileHandler,{'path':os.path.join(config.BASE_DIRS,'static/html')},'default_filename':'index.html'),然后在static/html添加
index.html
,main.html
等等 ,当用户访问http://127.0.0.1:8000/index.html
,http://127.0.0.1:8000/main.html
的时候就可以直接映射寻找文件了- path : 用来指定提供静态文件的根路径
- default_filename : 用来指定访同路由中末指明文件名时,默认提供的静态文件
数据库
tornado没有自带的ORM,对于数据库需要自己去适配
- 类似于aiohttp , 在应用启动时创建一个数据库链接实例,供各个 RequestHandler 使用
- 在 RequestHandler 中通过
self.application
来获取其应用对象
(如 :self.application.db
)
应用安全
Cookies
普通cookies
设置
1
self.set_cookie(name,value,domain=None,expires=None,path='/',expries_days=None,**kwargs)
- domain : 提交cookie时匹配的域名
- path : 提交cookie是匹配的路径
- expires : 有效期,可以是时间戳 , 时间元组 , datetime
- expries_days : 有效期天数 , 优先级低于expires
1
2
3
4class TestHandler9(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
self.set_cookie('name','hyl',expires=60*60*24)
self.write('hello')- 设置cookie实际上就是设置header的set_cookie字段
- 可以这么写 :
1
self.set_header('Set-Cookie','name=hyl; Path=/')
获取
1
2
3
4
5class TestHandler9(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
# '未登录'是default,当不存在时被赋值
self.get_cookie('name','未登录')
self.write('hello')清除:
- clear_cookie()
- clear_all_cookie()
1
2
3
4class TestHandler9(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
self.clear_cookie('name')
self.write('hello')删除名为name,井同时匹配 domain 和 path 的 cookie
- 执行清除 Cookie操作后,并不是立即删除浏器的cookie,而是给 cookie 值设置空,并改其有限期限为失效,
- 真正删除cookie是由浏克器白己去清理的
安全cookies
Cookie是存在客户浏览需的数据,很容易被篡改,Tornado提供了一种对 Cockie进行简易加密方式,来防止Cookie被篡改
设置 :
需要为应用配置一个用来给 Cookie进行混清加密的秘钥
生成密钥
1
2
3
4import base64
import uuid
print(base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes))
# 'fq/tqtjCRGG9kIAOtu2QQp0MbOtQ8kxdnYsS8lLQZlg='将密钥放入cookie_secret
1
2
3
4
5
6
7# config.py
settings = {
'debug' : True,
'cookie_secret':'fq/tqtjCRGG9kIAOtu2QQp0MbOtQ8kxdnYsS8lLQZlg='
'static_path':os.path.join(BASE_DIRS,'static'),
'template_path':os.path.join(BASE_DIRS,'templates'),
}set_secure_cookie
: 设置一个带有签名和时间的 cookie,防止cookie被伪造1
self.set_secure_cookie(name,value,expires_day=30,version=None,**kwargs)
示例:
1
2
3
4class TestHandler9(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
self.set_secure_cookie('name','hyl')
self.write('hello')对比
self.set_secure_cookie('name','hyl')
和self.set_cookie('name','hyl')
:后者明文显示
{name : hyl}
, 前者cookie值被加密:{name : 2|1:0|10:1567322102|4:name|4:aHls|c148ef76d7172335b55b52d0aaa0e00652be9ab8f57fba8a5679e9fb2735e1aa}
- 第一个字符
2
: 安全cookie的版本,默认使用版本2 1:0
: 1表示冒号后面有多少位10:1567322102
: 冒号后面是时间戳4:name
: cookies名4:aHls
: base64编码的cookies值- 最后是签名值,不带长度说明
- 第一个字符
获取:
1
self.get_secure_cookie(name,value=None,max_age_days=31,min_version=None)
1
2
3class TestHandler9(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
name = self.get_secure_cookie('name')max_age_days
不同于expires_days
:expires_days
: 设置浏览器中cookie的有效时间max_age_days
: 是过滤安全cookie的时间戳
XSRF
跨站请求伪造
cookie计数 : 通过对cookie计数获取
用户请求次数
1
2
3
4
5class TestHandler9(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
count = str(int(self.get_cookie('count',0)) + 1)
self.set_cookie('count',count)
self.write(count)一般上面的操作到放在了
prepare()
方法里:1
2
3
4
5
6
7class TestHandler9(tornado.web.RequestHandler):
def prepare(self):
count = str(int(self.get_cookie('count',0)) + 1)
self.set_cookie('count',count)
self.count = count
def get(self,*args,**kwargs):
self.write(self.count)上面这种cookie操作很容易被伪造请求.
原因 : 上面我们使用的是get请求.
所以我们一般使用post请求来传递重要数据
XSRF保护
本质 : 同源策略
使用流程 :
开启
xsrf_cookies
:1
2
3
4
5
6
7
8settings = {
'debug' : True,
'cookie_secret':'fq/tqtjCRGG9kIAOtu2QQp0MbOtQ8kxdnYsS8lLQZlg=',
# 开启保护
'xsrf_cookies':True,
'static_path':os.path.join(BASE_DIRS,'static'),
'template_path':os.path.join(BASE_DIRS,'templates'),
}在 post 中设置cookies
1
2
3
4
5
6
7
8class TestHandler9(tornado.web.RequestHandler):
def get(self,*args,**kwargs):
return self.render('index.html')
def post(self,*args,**kwargs):
count = str(int(self.get_cookie('count',0)) + 1)
self.set_cookie('count',count)
self.write(count)在模板中使用取消xsrf ,
{% module xsrf_form_html() %}
1
2
3
4{# index.html #}
<form action="/test9" method="post">
{% module xsrf_form_html() %}
<input type="submit" value="提交"/>- 在django中使用
{% csrf_token %}
- 在tornado中使用
{% module xsrf_form_html() %}
注意 :
- 使用XSRF保护必须同时设置
xsrf_cookies
和cookie_secret
- Django是因为已经默认帮我们在settings里设置好了 , 名字为
secret_key
- 在django中使用
{% module xsrf_form_html() %}
的本质 :就是最主流了防止CSRF功能手段
为浏览器设置
_xsrf
的安全cookies , 这个cookies 在关闭浏览器后会失效同时添加了隐藏的表单域
1
<input type="hidden" name="_xsrf" value="2|cd737260|f175d50f1fc88fa839f2d92205a200e3|1567326465">
在Ajax中使用:
1
2
3
4
5
6
7
8
9
10$.ajax({
url:'/postfile'
method:'POST',
data:...,
success:function(data){
alert('ok')
},
headers;{
'X-XSRFToken':getCookie('_xsrf')
})在StaticFileHandler中使用:
需要自动添加
_xsrf
的cookies1
2
3
4
5class StaticFileHandle(tornado.web.StaticFileHandler):
def __init__(self,*args,**kwargs):
# 添加token
self.xsrf_token
super().__init__(*args,**kwargs)1
2# 路由
(r'/(.*)$',index.StaticFileHandler,{'path':os.path.join(config.BASE_DIRS,'static/html','default_filename':'index.html')})
用户验证
指在收到用户请求后进行预先判断用户的认证状态是否登录),若验证通过则正常处理,否则进入到登录界面
使用
tornado.web.anthenticated装饰器
:
Tornade将确保这个方法的主体只有合法的用户才能调用使用:
1
2
3
4
5
6
7
8
9
10class LoginHandler(RequestHandler):
def gt(self,*args,**kwargs):
self.render(RequestHandler)
class HomeHandler(RequestHandler):
def get_current_user(self):
return False
def get(self,*args,**kwargs):
self.render('home.html')@tornado.web.authenticated
装饰器会调用get_current_user()
方法验证用户的逻辑应该写在
get_current_user()
方法中,如果该方法返回的为True说明验证成功,否则验证失败验证失败请求会重定向到配置中的
login_url
所指定的路由1
2
3
4
5
6
7
8settings = {
'debug' : True,
'cookie_secret':'fq/tqtjCRGG9kIAOtu2QQp0MbOtQ8kxdnYsS8lLQZlg=',
'xsrf_cookies':True,
'login_url':'/login',
'static_path':os.path.join(BASE_DIRS,'static'),
'template_path':os.path.join(BASE_DIRS,'templates'),
}