• 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源码分析

    详见:https://www.cnblogs.com/linagcheng/p/10401741.html

    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时就会保存
    
    
    
    
  • 相关阅读:
    Cocos 更新时反复杀进程,导致差异更新失效的Bug
    Cocos 编译android-studio
    Could not resolve all dependencies for configuration ':classpath'
    MAC Gradle 下载的问题
    命令行创建 keystore
    应用间共享文件 FileProvider
    Android jks 签名文件 生成
    Gradle 离线 安装
    信息
    Cocos Lua的Touch 点击事件添加
  • 原文地址:https://www.cnblogs.com/linagcheng/p/10396626.html
Copyright © 2020-2023  润新知