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