Flask框架(三)—— 请求扩展、中间件、蓝图、session源码分析

Flask框架(三)—— 请求扩展、中间件、蓝图、session源码分析

请求扩展、中间件、蓝图、session源码分析

一、请求扩展

1、before_request

类比django中间件中的process_request,在请求收到之前绑定一个函数做一些事情

#基于它做用户登录认证
@app.before_request
def process_request(*args,**kwargs):
    if request.path == '/login':
        return None
    user = session.get('user_info')
    if user:
        return None
    return redirect('/login')

2、after_request

类比django中间件中的process_response,每一个请求之后绑定一个函数,如果请求没有异常

@app.after_request
def process_response1(response):
    print('process_response1 走了')
    return response

3、before_first_request

第一次请求时会走,跟浏览器无关

@app.before_first_request
def first():
    pass

4、teardown_request

每次请求走,都会走它,包括视图函数出了异常

@app.teardown_request 
def ter(e):
    pass

5、errorhandler

路径不存在时404,服务器内部错误500

@app.errorhandler(404)
def error_404(arg):
    return "404错误了"

6、template_global

标签

@app.template_global()
def sb(a1, a2):
    return a1 + a2
# {{sb(1,2)}}

7、template_filter

过滤器,注意模板层中参数的位置,一个写前面,其余写后面

@app.template_filter()
def db(a1, a2, a3):
    return a1 + a2 + a3
# {{ 1|db(2,3)}}

二、中间件

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World!'
# 模拟中间件
class Md(object):
    def __init__(self,old_wsgi_app):
        self.old_wsgi_app = old_wsgi_app

    def __call__(self,  environ, start_response):
        print('开始之前')
        ret = self.old_wsgi_app(environ, start_response)
        print('结束之后')
        return ret

if __name__ == '__main__':
    # 把原来的wsgi_app替换为自定义的
    app.wsgi_app = Md(app.wsgi_app)
    app.run()
    

三、蓝图(blueprint)

对程序进行目录结构划分

1、不使用蓝图,自己分文件

目录结构:

-templates
-views
    -__init__.py
    -user.py
    -order.py
-app.py

app.py

from views import app
if __name__ == '__main__':
    app.run()

init.py

from flask import Flask,request
app = Flask(__name__)
# 不导入这个不行,容易产生交叉导入问题
from . import order
from . import user

user.py

from . import app
@app.route('/user')
def user():
    return 'user'

order.py

from . import app
@app.route('/order')
def order():
    return 'order'

2、使用蓝图之中小型系统

蓝图模板下载:
链接:https://pan.baidu.com/s/1nosJD2PCtk2jQa4d3wLL4g 
提取码:yhkq 
  • xxx = Blueprint('account', __name__,url_prefix='/xxx',)url_prifix 表示添加在原路由中加中间路由,此时须访问127.0.0.1/xxx/index才能访问

目录结构:

-flask_pro
    -flask_test
        -__init__.py
        -static
        -templates
        -views
            -order.py
            -user.py
     -manage.py 
        

__init__.py

from flask import  Flask
app=Flask(__name__)

# 3.注册蓝图到app中
from flask_test.views import user
from flask_test.views import order
app.register_blueprint(user.us)
app.register_blueprint(order.ord)

manage.py

from flask_test import  app
if __name__ == '__main__':
    app.run(port=8008)

user.py

from flask import Blueprint
# 1.创建蓝图
us=Blueprint('user',__name__,)
# 2.使用蓝图
@us.route('/login')
def login():
    return 'login'

order.py

from flask import Blueprint
ord=Blueprint('order',__name__)

@ord.route('/test')
def test():
    return 'order test'

3、使用蓝图之大型系统

蓝图模板下载:
链接:https://pan.baidu.com/s/1Ce2npcn2PTRsvrfHjG9b2A 
提取码:rnk9

4、总结

(1)xxx = Blueprint('account', name,url_prefix='/xxx') :蓝图URL前缀,表示url的前缀,在该蓝图下所有url都加前缀

(2)xxx = Blueprint('account', name,url_prefix='/xxx',template_folder='tpls'):给当前蓝图单独使用templates,向上查找,当前找不到,会找总templates

(3)蓝图的befort_request,对当前蓝图有效

(4)大型项目,可以模拟出类似于django中app的概念

四、threading.local

多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据存储

1、不用threading.local

# 不用local
from threading import Thread
import time
lqz = -1
def task(arg):
    global lqz
    lqz = arg
    # time.sleep(2)
    print(lqz)

for i in range(10):
    t = Thread(target=task,args=(i,))
    t.start()

2、threading.local使用

from threading import Thread
from threading import local
import time
from threading import get_ident
# 特殊的对象
lqz = local()
def task(arg):
    # 对象.val = 1/2/3/4/5
    lqz.value = arg
    time.sleep(2)
    print(lqz.value)
for i in range(10):
    t = Thread(target=task,args=(i,))
    t.start()

3、通过字典自定义threading.local(函数)

from threading import get_ident,Thread
import time
storage = {}
def set(k,v):
    ident = get_ident()
    if ident in storage:
        storage[ident][k] = v
    else:
        storage[ident] = {k:v}
def get(k):
    ident = get_ident()
    return storage[ident][k]
def task(arg):
    set('val',arg)
    v = get('val')
    print(v)

for i in range(10):
    t = Thread(target=task,args=(i,))
    t.start()

4、面向对象版

from threading import get_ident,Thread
import time
class Local(object):
    storage = {}
    def set(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}
    def get(self, k):
        ident = get_ident()
        return Local.storage[ident][k]
obj = Local()
def task(arg):
    obj.set('val',arg) 
    v = obj.get('val')
    print(v)
for i in range(10):
    t = Thread(target=task,args=(i,))
    t.start()

5、通过setattr和getattr实现

from threading import get_ident,Thread
import time
class Local(object):
    storage = {}
    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in Local.storage:
            Local.storage[ident][k] = v
        else:
            Local.storage[ident] = {k: v}
    def __getattr__(self, k):
        ident = get_ident()
        return Local.storage[ident][k]
obj = Local()
def task(arg):
    obj.val = arg
    print(obj.val)
for i in range(10):
    t = Thread(target=task,args=(i,))
    t.start()

6、每个对象有自己的存储空间(字典)

from threading import get_ident,Thread
import time
class Local(object):
    def __init__(self):
        object.__setattr__(self,'storage',{})
    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][k] = v
        else:
            self.storage[ident] = {k: v}
    def __getattr__(self, k):
        ident = get_ident()
        return self.storage[ident][k]
obj = Local()
def task(arg):
    obj.val = arg
    obj.xxx = arg
    print(obj.val)
for i in range(10):
    t = Thread(target=task,args=(i,))
    t.start()

7、兼容线程和协程(终极版)——flask中使用版本

try:
    from greenlet import getcurrent as get_ident
except Exception as e:
    from threading import get_ident
    
from threading import Thread
import time
class Local(object):
    def __init__(self):
        object.__setattr__(self,'storage',{})
    def __setattr__(self, k, v):
        ident = get_ident()
        if ident in self.storage:
            self.storage[ident][k] = v
        else:
            self.storage[ident] = {k: v}
    def __getattr__(self, k):
        ident = get_ident()
        return self.storage[ident][k]
obj = Local()
def task(arg):
    obj.val = arg
    obj.xxx = arg
    print(obj.val)
for i in range(10):
    t = Thread(target=task,args=(i,))
    t.start()

五、session源码分析

1.请求来了,生成一个空session放到ctx中:ctx = self.request_context(environ)
2.ctx.push():
    if self.session is None:
        session_interface = self.app.session_interface
        # 正常情况下session中有值了
        # 什么情况下没有:请求中没有cookie,session仍为空
        # 从cookie中取出value,(有解密过程)转成session对象
        
        self.session = session_interface.open_session(
            self.app, self.request
        )

        if self.session is None:
            # 生成一个空session
            self.session = session_interface.make_null_session(self.app)
3.请求走了:
    self.full_dispatch_request()
        1 执行before_request
        2 执行视图函数
        3 把session写入
            if not self.session_interface.is_null_session(ctx.session):
                self.session_interface.save_session(self, ctx.session, response)

六、session修改的坑

# 1.增加值
session['user']={'name':'aaa','age':18}

# 2.修改session的坑
session['user']['name']='bbb'
# 这样无法修改session,这样触发的是session['user']字典的__setitem__方法,没有modified,在save_session时没有保存,所以session并没有改变
                
# 解决方法一
session['user']={'name':'bbb','age':18}   
# 这样是在__setitem__方法中写了modified,并设置为True,在save_session时因为是True所以保存

# 解决方法二
session['user']['name']='bbb'
session.modified=True     # 修改完后,手动指定modified为True,这样再save_session时就会保存


博客内容仅供参考,部分参考他人优秀博文,仅供学习使用
posted @ 2019-03-21 19:13  zack赵康  阅读(146)  评论(0编辑  收藏