python基础-第十三篇-13.2Web框架之Tornado
- Tornado是非阻塞异步web frame,而且速度相当快,得力于其非阻塞的方式和对epoll的运用
- Tornado每秒可以处理数以千计的链接,所以它可以有效的处理C10K问题
下载安装
pip3 install tornado
- 源码安装
https:
/
/
pypi.python.org
/
packages
/
source
/
t
/
tornado
/
tornado
-
4.3
.tar.gz
框架应用
一、快速上手
# 第一步:导模块
import tornado.ioloop
import tornado.web # 第二步:创建类,必须继承tornado.web.RequestHandler,按照自己的业务逻辑重写get方法或post方法
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world") # 第三步:实例Application对象,构建路由系统
application = tornado.web.Application([
(r"/index", MainHandler),
]) if __name__ == "__main__":
# 第四步:socket运行起来
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
更多路径配置
'template_path': 'views', # html文件
'static_path': 'statics', # 静态文件(css,js,img)
'static_url_prefix': '/statics/',# 静态文件前缀
'cookie_secret': 'suoning', # cookie自定义字符串加盐
# 'xsrf_cookies': True, # 防止跨站伪造
# 'ui_methods': mt, # 自定义UIMethod函数
# 'ui_modules': md, # 自定义UIModule类
执行过程:
- 第一步:执行脚本,监听 8888 端口
- 第二步:浏览器客户端访问 /index --> http://127.0.0.1:8888/index
- 第三步:服务器接受请求,并交由对应的类处理该请求
- 第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
- 第五步:方法返回值的字符串内容发送浏览器
二、路由系统
路由系统其实就是url和类的对象关系,这里不同于其他框架,其他很多框架均是url对应函数,Tornado中每个url对应的是一个类
import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world") class StoryHandler(tornado.web.RequestHandler):
def get(self, story_id):
self.write("You requested the story " + story_id) class BuyHandler(tornado.web.RequestHandler):
def get(self):
self.write("buy.wupeiqi.com/index") application = tornado.web.Application([
(r"/index", MainHandler),
(r"/story/([0-9]+)", StoryHandler), #基于此实现分页功能
]) application.add_handlers('buy.wupeiqi.com$', [
(r'/index',BuyHandler), #这里添加2级域名,测试的话需要改本地host
]) if __name__ == "__main__":
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
三、模板引擎
模板引擎说简单点就是将原来的html的某些内容用一些特殊的字符串代替,然后再处理用户的不同请求时,将html的字符串替换掉,返回给用户新的一个字符串,这样就达到了动态的html的效果。
Tornado的模板支持“控制语句”和“表达语句”,控制语句格式{% python语句 %} 例如:{% for item in range(10)%},表达语句格式{{变量}} 比如:{{item}},对于控制语句在逻辑结尾的地方还要写上{% end %}
不仅提供通过UIMethod和UIModule来自定义方法和模块,而且Tornado本身就提供了一些方法,其中<link href="{{static_url("commons.css")}}" rel="stylesheet" /> static_url方法可以实现静态文件缓存(更新的内置方法见骚师博客)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>{{name}}</h1>
{% for item in user_list %}
<li>{{item}}</li>
{% end %}
</body>
</html>
index.html
import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html",name="alex",user_list=[11,22,33]) settings = {
'template_path':'views',
} application = tornado.web.Application([
(r"/index", MainHandler),
],**settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
自定义UIMethod和UIModule:通过模板语言的自定义功能,可以让你使用更加熟悉的python代码来实现动态的模板渲染,其中UIMethod中定义函数,UIModule中定义类
实现自定义方法三步走:
- 创建UIMethod.py UIModule.py,定义方法和类(方法定义的时候,必须传入self;类中必须要有render方法,功能代码实现就在这个方法里)
# uimethods.py def tab(self):
return 'UIMethod'
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule
from tornado import escape class custom(UIModule): def render(self, *args, **kwargs):
return escape.xhtml_escape('<h1>wupeiqi</h1>')
#return escape.xhtml_escape('<h1>wupeiqi</h1>')
- 导入创建文件,在settings里注册
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web
from tornado.escape import linkify
import uimodules as md
import uimethods as mt class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html') settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'ui_methods': mt,
'ui_modules': md,
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8009)
tornado.ioloop.IOLoop.instance().start()
- 模块中调用 方法:{{ func() }} 类:{% module 类名() %}
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>hello</h1>
{% module custom(123) %}
{{ tab() }}
</body>
四、模板继承和静态缓存
将一些公用的html,css等写到通用的文件,然后通过继承,就可以获取母版的内容,而继承的html里面只需要写特有的东西,模板继承的功能非常实用,而静态缓存则可以减少相应的请求资源。
母版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="{{static_url('css/chouti.css')}}" type="text/css" rel="stylesheet"> // 通过static_url引用静态文件
{% block css %} {% end %}
</head>
<body>
<div class="header">
<div class="header-content">
{% if user_info['is_login'] %}
<div class="account">
<a href="#">{{user_info['username']}}</a>
<a href="/logout">退出</a>
</div>
{% else %}
<div class="account">
<a href="http://127.0.0.1:8888/register">注册</a>
<a href="http://127.0.0.1:8888/login">登陆</a>
</div>
{% end %}
</div>
</div>
<div class="content">
{% block body %}
{% end %}
</div>
<a class="back-to-head" href="javascript:scroll(0,0)"></a>
{% block js %} {% end %}
<script> </script>
</body>
</html>
子版
{% extends '../base/layout.html' %}
{% block css %}
<link href="{{static_url('css/css/common.css')}}" rel="stylesheet">
<link href="{{static_url('css/css/login.css')}}" rel="stylesheet">
{% end %} {% block body %} {% end %} {% block js %}
<script src="{{static_url('js/jquery-1.12.4.js')}}"></script>
<script src="{{static_url('js/login.js')}}"></script>
{% end %}
五、Xss和csrf
Xss跨站脚本攻击
恶意攻击者往web页面里插入恶意script代码,当用户浏览该页时,嵌入web里面的script代码会被执行,从而达到恶意攻击用户的特殊目的
csrf跨站请求伪造(对post请求限制)
get请求的时候,会给浏览器发一个id,浏览器post请求的时候,携带这个id,然后服务端对其做验证,如果没有这个id的话,就禁止浏览器提交内容
在Tornado里需要在settings里配置“xsrf_cookies”:True,如果这样做,Tornado将拒绝浏览器请求参数中不包含正确的_xsrf值的post/put/delete请求,并禁止其访问
settings = {
"xsrf_cookies": True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
配置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="/sss/jquery-1.12.4.js"></script>
<!--<script src="{{ static_url('jquery-1.12.4.js') }}" ></script>-->
</head>
<body>
<!--{{ xsrf_form_html() }}-->
{% raw xsrf_form_html() %} <input type="button" value="ajax_csrf" onclick="SubmitCsrf();"> <script> function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[] : undefined;
} function SubmitCsrf() {
var nid = getCookie('_xsrf');
console.log(nid);
$.post({
url: '/csrf',
data:{'k1':'v1', "_xsrf":nid},
success:function (callback) {
console.log(callback);
}
});
}
</script>
</body>
</html>
六、上传文件
1、Form表单上传
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>上传文件</title>
</head>
<body>
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<input name="fff" id="my_file" type="file" />
<input type="submit" value="提交" />
</form>
</body>
</html>
index.html
import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
#获取文件方法self.request.files
file_metas = self.request.files["fff"]
# print(file_metas)
#[{'filename':'文件名','body':'文件内容']
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body']) settings = {
'template_path': 'views',
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
2、AJAX上传
HTML - XMLHttpRequest
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = document.getElementById("img").files[0]; //创建Formdata对象,作为文件对象的载体
var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); var xhr = new XMLHttpRequest();
xhr.open("post", '/index', true);
xhr.send(form);
}
</script>
</body>
</html>
HTML - jQuery
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = $("#img")[0].files[0];
var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); $.ajax({
type:'POST',
url: '/index',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success: function(arg){
console.log(arg);
}
})
}
</script>
</body>
</html>
import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
#获取文件方法self.request.files
file_metas = self.request.files["fff"]
# print(file_metas)
#[{'filename':'文件名','body':'文件内容']
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body']) settings = {
'template_path': 'views',
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen()
tornado.ioloop.IOLoop.instance().start()
py
七、验证码
验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面
- 安装图像处理模块:pip3 install pillow
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="statics/jquery-1.12.4.js"></script>
</head>
<body>
<form action="login" method="post">
<input type="text", name="code">
<img src="/check_code" onclick="ChangeCode();" id="imgcode">
<input type="submit" value="submit">
<span>{{status}}</span>
</form>
<script>
function ChangeCode() {
var code = document.getElementById('imgcode');
code.src += '?'
}
</script>
</body>
</html>
html
class CheckCodeHandler(BaseHandler):
def get(self, *args, **kwargs):
import io
import check_code
mstream = io.BytesIO()
img, code = check_code.create_validate_code()
img.save(mstream, 'GIF')
self.session['CheckCode'] = code
self.write(mstream.getvalue())
py
八、自定义分页类
分页功能十分常见的,所以整理成一个类,当做一个小组件来使用是非常有必要的
在前端注意因为xxs不能显示页面的情况,{% raw data %}
class Pagenation:
def __init__(self, current_page, all_item, each_item): #all_pager总页数,c余数
all_pager, c = divmod(all_item, each_item)
#余数不为0,总页数要加1
if c > 0:
all_pager += 1
#如果客户在url里没输入页码,默认当前页为1
if current_page == '':
current_page = 1
self.current_page = int(current_page) # 当前页
self.all_pages = all_pager # 总的页面数
self.each_item = each_item # 每页显示的item数 @property
def start_item(self): # 当前页的起始item位置
return (self.current_page - 1) * self.each_item @property
def end_item(self): # 当前页结束item位置
return self.current_page * self.each_item @property
def start_end_span(self): # 获取开始和结束页的具体数字
if self.all_pages < 10:
start_page = 1 # 起始页
end_page = self.all_pages + 1 # 结束页
else: # 总页数大于10
if self.current_page < 5:
start_page = 1
end_page = 11
else:
if (self.current_page + 5) < self.all_pages:
start_page = self.current_page - 4
end_page = self.current_page + 5 + 1
else:
start_page = self.all_pages - 10
end_page = self.all_pages + 1
return start_page, end_page def generate_str_page(self):
list_page = []
start_page, end_page = self.start_end_span if self.current_page == 1: # 上一页
prev = '<li><a class="pre-page" href="javascript:void(0);">上一页</a></li>'
else:
prev = '<li><a class="pre-page" href="/index/%s">上一页</a></li>' % (self.current_page - 1,)
list_page.append(prev) for p in range(start_page, end_page): # 1-10
if p == self.current_page:
temp = '<li><a class="li-page" href="/index/%s">%s</a></li>' % (p, p)
else:
temp = '<li><a href="/index/%s">%s</a></li>' % (p, p)
list_page.append(temp) if self.current_page == self.all_pages: # 下一页
nex = '<li><a class="next-page" href="javascript:void(0);">下一页</a></li>'
else:
nex = '<li><a class="next-page" href="/index/%s">下一页</a></li>' % (self.current_page + 1,)
list_page.append(nex) # 跳转
jump = """<input type='text' /><a onclick="Jump('%s',this);">GO</a>""" % ('/index/')
script = """<script>
function Jump(baseUrl,ths){
var val = ths.previousElementSibling.value;
if(val.trim().length>0){
location.href = baseUrl + val;
}
}
</script>"""
list_page.append(jump)
list_page.append(script)
str_page = "".join(list_page)
return str_page
更多详细内容请见骚师博客:http://www.cnblogs.com/wupeiqi/articles/5702910.html
王冬web详解http://www.cnblogs.com/Wxtrkbc/p/5704022.html
Tornado组件http://www.cnblogs.com/Wxtrkbc/p/5710471.html
python基础-第十三篇-13.2Web框架之Tornado的更多相关文章
- python基础-第十三篇-13.1web框架本质
基础与概念 众所周知,对于所有的web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端 web框架分两类:一类是包括socket和业务逻辑(tornado),另一 ...
- Python基础【第一篇】
一.Python简介 Python的创始人(Guido von Rossum 荷兰人),Guido希望有一种语言既能像C一样方便地调用操作系统的功能接口,也能像shell脚本一样,轻松地实现编程,A ...
- Python 学习 第十三篇:数据的读写-文件、DataFrame、json
Python的文件是一个重要的对象,使用open()函数来打开文件,创建文件对象,进行文件的读写操作.当数据用于交换信息时,通常需要把数据保存为有格式的文本数据,可以保存为有特定的行分隔符和列分隔符的 ...
- Python 基础学习 总结篇
Python 基础学习总结 先附上所有的章节: Python学习(一)安装.环境配置及IDE推荐 Python学习(二)Python 简介 Python学习(三)流程控制 Python学习(四)数据结 ...
- python基础-第六篇-6.2模块
python之强大,就是因为它其提供的模块全面,模块的知识点不仅多,而且零散---一个字!错综复杂 没办法,二八原则抓重点咯!只要抓住那些以后常用开发的方法就可以了,哪些是常用的?往下看--找答案~ ...
- python基础知识第一篇(认识Python)
开发语言: 高级语言:python java php c++ 生成的字节码 字节码转换为机器码 计算机识别运行 低级语言:C 汇编 生成的机器码 PHP语言:适用于网页,局限性 Python,Java ...
- python、第七篇:ORM框架SQLAlchemy
一 介绍 SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取 ...
- Python基础第三篇
一.collections系列 Counter是对字典类型的补充,用于追踪值的出现次数,具备字典的所有功能 + 自己的功能 1.计数器Counter import collections a='aba ...
- python基础-第十篇-10.1HTML基础
htyper text markup language 即超文本标记语言 超文本:就是指页面内可以包含图片.链接,甚至音乐,程序等非文字元素 标记语言:标记(标签)构成的语言 网页==HTML文档,由 ...
随机推荐
- RS232接口
想用下板子,却发现板子和USB转串口线都是母口,无耐只能自己用线将对应的管脚连起来. 结果测试的时候发现,板子能发不能收.将板子串口的23连起来,回环正常.电脑USB转串口线上的23连起来也回环正常. ...
- ORACLE中NVL和COALESCE的区别
nvl(COMMISSION_PCT,0)如果第一个参数为null,则返回第二个参数如果第一个参数为非null,则返回第一个参数 COALESCE(EXPR1,EXPR2,EXPR3...EXPRn) ...
- java printf long
System.out.printf("%d\n", 1000000000000000000L);
- 跟着百度学PHP[15]-会话控制session的工作机制
COOKIE和SESSION的两大区别: cookie是存储与客户端 session是存储与服务端 需要开启session的时候需要使用session_start开启,且session的开头不能拥有任 ...
- 交叉编译移植openssl
交叉编译openssl静态库步骤: 1.解压源码 tar xf openssl-1.1.1a.tar.gz 2.进入到解压后的源码目录 cd openssl-1.1.1a/ 3.配置Makefile ...
- ubuntun 下安装 node-v0.10.26
sudo apt-get install g++ curl libssl-dev apache2-utils wget http://nodejs.org/dist/v0.10.26/node-v0. ...
- Scrapy中用xpath/css爬取豆瓣电影Top250:解决403HTTP status code is not handled or not allowed
好吧,我又开始折腾豆瓣电影top250了,只是想试试各种方法,看看哪一种的方法效率是最好的,一直进行到这一步才知道 scrapy的强大,尤其是和selector结合之后,速度飞起.... 下面我就采用 ...
- spring-boot启动注解@EnableAutoConfiguration
springboot很多依赖插件是只要有依赖,就会读取相关配置,如果读取不到,就会使用默认的,可能会报错,但是又在项目中不好排除就可以使用 @EnableAutoConfiguration 注解.启动 ...
- 第二百零八节,jQuery EasyUI,SplitButton(分割按钮菜单)组件
jQuery EasyUI,SplitButton(分割按钮)组件 学习要点: 1.加载方式 2.属性列表 3.方法列表 本节课重点了解 EasyUI 中 SplitButton(分割按钮)组件的使用 ...
- GZipStream
命名空间: System.IO.Compression 说明: 此类表示 GZip 数据格式,它使用无损压缩和解压缩文件的行业标准算法.这种格式包括一个检测数据损坏的循环冗余校验值.GZip 数据格式 ...