web框架
2018/07/13 day100
一、Web框架
- bottle
- flask
- tornado
基本的http请求周期:用户请求-》服务器的路由系统-》业务逻辑处理(操作数据库,原生或者orm)(模板渲染)。django还有中间件。
它们的特点:
# 三大组件
- 路由系统
- 控制器(含模版渲染)
- 数据库操作
# 微型框架
- 依赖第三方写的socket,都要遵循WSGI
- 本身功能少
# 安装
pip3 install bottle pip3 install flask pip3 install tornado
1. bottle
简单示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bottle import template, Bottle
root = Bottle()
@root.route('/hello/')
def index():
return "Hello World"
# return template('<b>Hello {{name}}</b>!', name="Alex")
root.run(host='localhost', port=8080)
a. 路由系统
- 静态:@root.route('/hello/')
- 动态:支持正则表达式、数字
@root.route('/wiki/<pagename>') #pagename变量
@root.route('/object/<id:int>') #int检测是否整数,然后传给id
@root.route('/show/<name:re:[a-z]+>') #name参数,re后面是正则
@root.route('/static/<path:path>') #静态资源配置路径,传给path参数,root是具体的路径
- 方法:@root.route('/hello/', method='POST') #允许什么方式访问
- 二级:路由分发
from framwork_bottle import app01
root.mount('app01', app01.app01) #就指向了app01.py文件的路由
app01 = Bottle()
@app01.route('/hello/', method='GET')
b. 视图
- 获取用户内容 request
request.headers #请求头信息 request.query #get请求信息 request.forms #post请求信息 request.files #上传文件信息 request.params #get和post请求信息 request.GET #get请求信息 request.POST #post和上传信息 request.cookies #cookie信息 request.environ #环境相关相关
- 数据库操作
- 文件操作
- ...
- 返回给用户内容return ""
- 模版引擎渲染
单值: {{name}}
单行Python代码 % s1 = "hello"
Python代码块 <% %>
Python、Html混合 % if True: % end
函数include:% include('header.tpl', title='Page Title') #title参数
函数rebase:% rebase('base.tpl', title='Page Title') #相当于dajngo的{% block css %}{% endblock %}
defined(name): #检查当前变量是否已经被定义,已定义True,未定义False
get(name, default=None): #获取某个变量的值,不存在时可设置默认值
setdefault(name, default): #如果变量不存在时,为变量设置默认值
自定义函数:wupeiqi=custom {{ wupeiqi() }} #custom是定义的函数名,传给wupeiqi
xss安全:!相当于mark_safe
c. WSGI进行配置
socket基于wsui接口来实现,请求先到socket,再转交给web框架
root.run(host='localhost', port=8080, server='wsgiref')
默认server="wsgiref",即:使用Python内置模块wsgiref,如果想要使用其他时,则需要首先安装相关类库,然后才能使用
2.flask
简单示例:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
a.路由系统
@app.route('/user/<username>') #正则
@app.route('/post/<int:post_id>') #int传给post_id
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>') #.*除换行符外所有
@app.route('/login', methods=['GET', 'POST']) #方法
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter, #('/<any(about, help, imprint, class, "foo,bar"):page_name>')
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
b.模板
Flask使用的是Jinja2模板,所以其语法和Django无差别
return render_template()
自定义模板:{{k4()|safe}} k4=jinxi def jinxi():
c.请求数据
request.method request.args request.form request.values request.files request.cookies request.headers request.path request.full_path request.script_root request.url request.base_url request.url_root request.host_url request.host
d.数据库连接:
#python的ORM框架:SQLAchemy
#pymysql
e.响应:
字符串 return "index"
模板引擎 return render_template("index.html")
重定向 return redirect(url_for('login')) #login相当于url的name,flask中与函数同名
设置相应信息 response = make_response() response.set_cookie response.headers['X-Something'] = 'A value'
return response
f.Session:
加密放在cookie中,需要先设置app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
设置:session['username'] = 'xxx'
删除:session.pop('username', None)
g.基于wsgi自定制中间件
class Foo: def __init__(self,w): self.w = w def __call__(self, environ, start_response): obj = self.w(environ, start_response) #前后即可定制操作 return obj if __name__ == "__main__": # app.wsgi_app = my_wsgi_app app.wsgi_app = Foo(app.wsgi_app) #加括号执行Foo的call方法 app.run()
这样返回的wsgi_app还是内置的,但obj的前后可以自定制一些操作。django的中间件也是这样定制的
h.message
message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除
flash(v) #设置值
{% with messages = get_flashed_messages() %} #模板中取值,一次即删
i. flask程序目录
views -__init__.py #import config,在这里创建app=Flask(__name__,template_folder = config.template_folder), # 这样所有的py文件用的是同一个单例app -account.py #from . import app,.表示本级目录, -home.py templates -index.html config.py #写上template_folder = 'templates' manage.py #import views即可,views.app.run()
3. Tornado
- 异步非阻塞的框架 Node.js
模版语言:
Django: ORM,模板引擎
bottle: 模板引擎
SqlAchmey: ORM
JinJa2: 模板引擎,和django一样
2018/07/14 day101 Tornado
异步非阻塞的框架,遇到IO等待的时候可以处理其他,select
简单示例:
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
application = tornado.web.Application([
(r"/index", MainHandler),
])
if __name__ == "__main__":
#创建socket对象
#将对象添加到select或epoll中
application.listen(8888)
#将select或epoll开始死循环,while true
tornado.ioloop.IOLoop.instance().start()
1.路由系统
Tornado中每个url对应的是一个类。
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
2.模板
控制语句: {% %} {% end %}
表达语句: {{ }}
UIMethod:
UIMethod:
def tab(self):
return 'UIMethod'
{{ tab() }}
UIModule:
class custom(UIModule):
def render(self, *args, **kwargs):
return escape.xhtml_escape('<h1>wupeiqi</h1>') #不显示html格式
{% module custom(123) %}
3.静态文件
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
application = tornado.web.Application([(r"/index", MainHandler),], **settings)
4.cookie
self.set_cookie("mycookie", "myvalue")
self.get_cookie("mycookie")
5.session
二、Web组件的定制
1. Session
请求到来
请求到来
- 用户浏览器设置cookie {'session_id':随机字符串}
-
session = {
随机字符串: {login:true}
随机字符串: {login:true}
}
a.定义session.py文件,编写一个保存键值对的类和容器
import uuid
class Session(object):
container = {}
def __init__(self,handler):
# 获取用户cookie,有则不操作,没有就生成随机字符串,写给用户和保存在session
nid = handler.get_cookie('session_id')
if nid:
if nid in self.container: #有nid在session中,每次访问时都更新超时时间
pass
else:
nid = str(uuid.uuid4()) #有nid不在session中,访问时创建sessionid,set_cookie
self.container[nid] = {}
else:
nid = str(uuid.uuid4()) #没有有nid,访问时创建sessionid,set_cookie
self.container[nid]={}
handler.set_cookie('session_id', nid, max_age=1000)
self.nid = nid # 当前用户的随机字符串
self.handler = handler # 用户所有的请求信息
def __setitem__(self, key, value): # 设置session类的值时自动执行
self.container[self.nid][key] = value
def __getitem__(self,key):
return self.container[self.nid].get(key) #如果没有返回none
def __delitem__(self, key):
del self.container[self.nid][key]
b.创建session类的对象,传键和值进行设置和获取
class BaseHandler(object): #所有的视图函数执行前先执行这个函数:创建session对象给全局self def initialize(self): # 获取用户cookie,有则不操作,没有就生成随机字符串,写给用户和保存在session from session import Session self.session = Session(self) #传给session方法,指IndexHandler等 super(BaseHandler, self).initialize() #不影响其他类的同名方法执行,先执行这个,再执行他们的
c.视图函数中设置和获取session
if v == 'root':
self.session['is_login']=True #设置
if self.session['is_login']: #获取
self.write("Hello, world")
d.不同储存类型的session选择
import session
cls = getattr(session,config.session_key) #config.py文件中session_key = 'Session'
self.session = cls(self) #传给session方法,指IndexHandler等
e.哈希算法选择服务器存储
将session的nid转数字,哈希算法分配服务器机台
2. Form组件
a. 表单验证
- 写Form类
- 定义Form类中的字段
- 用户发送
- obj = Form(请求数据)
obj.is_valid()
obj.cleaned_data
示例:
示例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
import re
class StringField: #定义字段
def __init__(self,name):
self.rex = '^\w+$'
self.name = name
self.value = ''
self.error = ''
def __str__(self): #输出类的对象时显示这个
return "<input type='text' name=%s value='%s'/>" %(self.name,self.value)
class EmailField:
def __init__(self,name):
self.rex = '^\w+@.*$'
self.name = name
self.value = ''
self.error = ''
def __str__(self):
return "<input type='text' name=%s value='%s'/>" %(self.name,self.value)
class LoginForm: #Form组件
def __init__(self):
self.user = StringField(name='user')
self.email = EmailField(name='email')
def is_valid(self,handler):
value_dict = {}
flag = True
for k,v in self.__dict__.items(): #对象里的所有字段
inp = handler.get_argument(k)
rex = re.match(v.rex,inp)
v.value = inp
if rex:
value_dict[k]=inp
else:
v.error = "%s错误了" % k
flag = False
return flag,value_dict
class LoginHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
obj = LoginForm()
self.render('login.html',**{'obj':obj}) #调用字段的str方法,raw原生显示
def post(self, *args, **kwargs):
obj = LoginForm()
valid,value_dict = obj.is_valid(self)
if valid:
print(value_dict)
else:
self.render('login.html', **{'obj': obj})
settings = {'static_path':'static','static_url_prefix':'/css/','template_path':'templates'}
application = tornado.web.Application([
(r"/login", LoginHandler),
],**settings)
if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
三、tornado异步非阻塞原理:
2018/07/15 day102
1. 运算
v = 1 and 2 or 3 an 4
2. 基本数据类型
如字符串有几个方法
3. 列表生成式
4. 作用域
python与js类似
5. 函数的参数和返回值
可变类型当参数,一改整体改
6. 迭代器和生成器
7. 面向对象
- 继承
- metaclass
8. 异步IO模块
9. 异步非阻塞Web框架
=====================================================
支持异步非阻塞Web框架 - Tornado,Nodejs
异步IO模块:
我们作为客户端向服务端发起“并发”请求,select监听socket是否已经有变化
异步非阻塞框架:
服务器收到非计算型即IO请求挂起的同时,处理其他请求,当IO请求有结果,其他服务器返回时,再返回给IO请求,断掉连接。
future = Future()
1. 挂起当前请求,线程可以处理其他请求 *****
2. future设置值,当前挂起的请求返回
3种情况:
- 超时时间到,往future设置值,请求结束,等待同时可以处理其他请求
- 请求来了,再往远处发请求,远处有返回时结束当前请求
- 等待另一个请求设置future值终止
自定义Web框架:
- 同步
- 异步
一、Tornado异步非阻塞
1.设置超时时间
from tornado import gen
from tornado.concurrent import Future
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
future = Future()
tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.doing)
yield future
def doing(self, *args, **kwargs):
self.write('async')
self.finish()
2.往远处发请求
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
from tornado import httpclient
http = httpclient.AsyncHTTPClient()
yield http.fetch("http://www.google.com", self.done)
def done(self, response):
print('完事')
self.finish('666')
3.发请求设置future
future = None
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
global future
future = Future()
future.add_done_callback(self.done)
yield future
def done(self, response):
print('完事')
self.finish('666')
class IndexHandler(tornado.web.RequestHandler):
def get(self):
global future
future.set_result(None) #设置
self.write("Hello, world")
二、自定义web框架
1.同步
import socket
import select
class HttpRequest(object):
"""
用户封装用户请求信息
"""
def __init__(self, content):
"""
:param content: 请求头、请求体
"""
self.content = content
self.header_bytes = bytes()
self.header_dict = {}
self.body_bytes = bytes()
self.method = ""
self.url = ""
self.protocol = ""
self.initialize()
self.initialize_headers()
def initialize(self):
temp = self.content.split(b'\r\n\r\n', 1) #分割请求体、头
if len(temp) == 1:
self.header_bytes += temp
else:
h, b = temp
self.header_bytes += h
self.body_bytes += b
header_flag = True
@property
def header_str(self):
return str(self.header_bytes, encoding='utf-8')
def initialize_headers(self):
headers = self.header_str.split('\r\n')
first_line = headers[0].split(' ')
if len(first_line) == 3:
self.method, self.url, self.protocol = headers[0].split(' ')
for line in headers:
kv = line.split(':')
if len(kv) == 2:
k, v = kv
self.header_dict[k] = v
def index(request):
return 'xxxx'
def main(request):
return 'main'
routers = [
('/index/',index),
('/main/',main),
]
def run():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 9999,))
sock.setblocking(False)
sock.listen(128)
inputs = []
inputs.append(sock)
while True:
rlist,wlist,elist = select.select(inputs,[],[],0.05)
for r in rlist:
if r == sock:
'''新请求到来'''
conn,addr = sock.accept()
conn.setblocking(False) #设置客户端socket非阻塞
inputs.append(conn)
else:
"""客户端发来数据"""
data = b"" #包含请求头、请求体
while True:
try:
chunks = r.recv(1024) #接收字节类型
data += chunks
except Exception as e:
chunks = None
if not chunks:
break
request = HttpRequest(data)
# print(request.url)
# print(request.method)
# print(request.header_dict)
# print(request.body_bytes)
# 1. 请求头获取url
# 2. 路由匹配,获取指定函数
import re
flag = False
func = None
for route in routers:
if re.match(route[0],request.url):
flag = True
func = route[1]
break
if flag:
result = func(request)
r.sendall(bytes(result,encoding='utf-8'))
else:
r.sendall(b'404')
# 3. 执行函数,获取返回值
# 4.r.sendall(b'asd')
inputs.remove(r)
r.close()
if __name__ =='__main__':
run()
总结:
select是IO多路复用,监听状态变化,本质还是同步
2.异步:Future
重点:return Future时pass,放到队列中,for循环监听Future的self.result值是否变化,变化了返回并close
变化可以是timeout作if判断,设置值;
请求的函数中设置(global一下Future);
远处请求返回设置一下。
代码:
a.发送请求设置
class Future(object): def __init__(self): self.result = None F = None #全局变量 def index(request): global F F = Future() return F def main(request): return 'main' def stop(request): global F F.result = b'sda' #设置 return 'stop'
循环监听:
for conn in async_request_dict.keys(): future = async_request_dict[conn] if future.result: #监听值是否变化 conn.sendall(future.result) conn.close() del async_request_dict[conn] inputs.remove(conn) else: pass
b.超时
class Future(object): def __init__(self,timeout): self.result = None self.timeout = timeout self.start_time = time.time() def index(request): f = Future(5) return f
循环:
for conn in async_request_dict.keys(): future = async_request_dict[conn] start_time = future.start_time timeout = future.timeout ctime = time.time() if (start_time + timeout)<= ctime: future.result = b'timeout' if future.result: conn.sendall(future.result) conn.close() del async_request_dict[conn] inputs.remove(conn) else: pass
浙公网安备 33010602011771号