# 蓝图、请求扩展和中间件

message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。

  • # 简单演示
    from flask import Flask,flash,get_flashed_messages
    
    app = Flask(__name__)
    app.secret_key = "DFSFerw"
    
    @app.route('/get')
    def get():
        data = get_flashed_messages()  #从那个地方拿值,取完一个值后,所有的都要清空
        print(data)   # 第一次['666', '777', '666']  第二次 []
        return 'Hello,world'
    
    @app.route('/set')
    def set():
        flash('666')   # 向某个地方设置了一个值,其实时结合session中设置了一个值
        return '设置成功'
    
    @app.route('/set2',endpoint='n2')
    def set():
        flash('777')   # 访问一次给那个地方设置一个值
        return '设置成功'
    
    if __name__ == '__main__':
        app.run()
    
    • 应用场景:比如视图函数重定向到另一个函数,记录前一个视图函数的一些参数
  • flash的附加功能:单独的使用一次的功能并不能满足我们的要求,也就是说,如果我们可以对添加进的数据进行分类,就更好了

    @app.route('/get')
    def get():
        data = get_flashed_messages(category_filter=['xxx'])  # 获取分类的数据
        print(data)   # 第一次['777']  第二次 []
        return 'Hello,world'
    
    @app.route('/set')
    def set():
        flash('666')  
        return '设置成功'
    
    @app.route('/set2',endpoint='n2')
    def set(): 
        flash('777',category='xxx')   #设置分类
        return '设置成功'
    

    注意: 由于flash是基于session做的,所以消息的特性也是符合session的,也就是说,如果你设置了两个值进去,突然服务端挂了,重新链接时,仍旧能取出数据

# 请求扩展

  • **简单实例:**主要是针对before_request、after_requset函数进行学习

    from flask import Flask,request
    app = Flask(__name__)
    
    @app.before_request
    def process_request(*args,**kwargs):  # 请求进来的时候执行,如果有return,直接返回结果给浏览器,不往后走,
        print('请求之前')
        return None   #返回None才能往后走
    
    @app.after_request
    def process_response(response):
        print('走了')
        return response
    
    @app.route('/index2',endpoint='n2')
    def a():
        print('index')
        return 'index视图函数'
        
    if __name__  == '__main__':
        app.run()
        
     #访问index2结果:
    请求之前
    index
    走了
    
  • # 多个before_request、after_requset执行时
    @app.before_request
    def process_request1(*args,**kwargs):  # 请求进来的时候执行,如果有return,直接返回结果给浏览器,不往后走,
        print(' process_request1 请求之前')
        return None   #返回None才能往后走
    
    @app.before_request
    def process_request2(*args,**kwargs):  # 请求进来的时候执行,如果有return,直接返回结果给浏览器,不往后走,
        print(' process_request2 请求之前')
        return None   #返回None才能往后走
    
    @app.after_request
    def process_response1(response):
        print('process_response1( 走了')
        return response
    
    @app.after_request
    def process_response2(response):
        print('process_response2 走了')
        return response
    
    @app.route('/index2',endpoint='n2')
    def a():
        print('index')
        return 'index视图函数'
    
     #访问index2结果:
    process_request1 请求之前
    process_request2 请求之前
    index
    process_response2 走了
    process_response1 走了
    
  • **定制报错信息:**当发生报错信息的时候,比如常见的404、500等,我们就可以使用自己得定制的界面

    @app.errorhandler(404)  #这里是定制的404错误,完全可以定制其他的错误码
    def error_404(args):
        return '404错误'   #访问不存在的网址时会报此错误
    
  • **before_first_request:**只有第一次请求过来的时候才执行这函数

    @app.before_first_request
    def process_request1(*args,**kwargs):
        print(' 第一次')  #第二次刷新就不走了
        return None
    

# 中间件

Flask的中间件主要是针对整个项目的入口,我们可以通过类的重新封装来扩展功能。

# 梳理Flask项目启动流程:
  • 项目启动执行app.run() 函数,点击run函数,在其中执行了

    run_simple(host, port, self, **options)  # 这里出自动触发self(),也就是触发了app的__call__方法
    
  • 查看__call__方法

     def __call__(self, environ, start_response):
            """The WSGI server calls the Flask application object as the
            WSGI application. This calls :meth:`wsgi_app` which can be
            wrapped to applying middleware."""
            return self.wsgi_app(environ, start_response) #environ是包含了所有请求的数据,start_response是一个发送HTTP响应的函数。
    
  • 根据以上我们可以扩展中间件

    from flask import Flask, flash, redirect, render_template, request
     
    app = Flask(__name__)
    app.secret_key = 'some_secret'
     
    @app.route('/')
    def index1():
        return render_template('index.html')
     
    @app.route('/set')
    def index2():
        v = request.args.get('p')
        flash(v)
        return 'ok'
     
    class MiddleWare:
        def __init__(self,wsgi_app):
            self.wsgi_app = wsgi_app
     
        def __call__(self, *args, **kwargs):
     		print('中间件执行之前')
            ret = self.wsgi_app(*args, **kwargs)
            print('中间件执行之后')
            return ret
     
    if __name__ == "__main__":
        app.wsgi_app = MiddleWare(app.wsgi_app) #自定义自己的wsgi_app方法
        app.run(port=9999)
    

# 蓝图

当我们的逻辑代码开始增多后改怎么办?一个py文件显然不能承载太多,所以我们可以分写在view函数中

  • 相应的代码:

     #account.py文件
    from . import app
    @app.route('/login')
    def login():
        return 'login'
    
    @app.route('/logout')
    def logout():
        return 'logout'
        
     -------------------------
     #order.py文件
    from . import app
    @app.route('/order')
    def order():
        return 'order'
    
    --------------------------
    from . import app
    @app.route('/user')
    def user():
        return 'user'
    
    ---------------------------
     #init.py文件
    from flask import Flask,request
    app = Flask(__name__)
    
    from . import order  #必须导入每个文件,否则不会执行相应的py文件相当于没有加载app的附加路由
    from . import user
    from . import account
    
    ---------------------------
     # 主文件,a2.py
    from views import app
    if __name__ == '__main__':
        app.run()
    
# 使用蓝图帮我们生成目录结构

原理和上面的方式类似,但是根据全面

  • 常见报错信息

     报错:AttributeError: 'function' object has no attribute 'name'
    
    原因是:
    from flask import Blueprint
    order = Blueprint('order',__name__)  # 相当于app一样,知识没有run方法,只能注册给app
    
    @orders.route('/order',)
    def order():
        return 'order'
    
     #原因是Blueprint的第一个参数和接受变量名相互重复了,修改变量名即可
     #orders = Blueprint('order',__name__)     
    
  • # 蓝图的目录结构
  • # 蓝图文件部分
     #account.py文件
    from flask import Blueprint
    
    account = Blueprint('account',__name__)  # 相当于app一样,知识没有run方法,只能注册给app
    
    @account.route('/login',methods=['GET',])
    def login():
        return 'account 中的 login'
    
    @account.route('/logout')
    def logout():
        return 'logout'
        
    ------------------------------------
     #order.py文件
    from flask import Blueprint
    
    orders = Blueprint('order',__name__,url_prefix = '/order')  # 相当于app一样,知识没有run方法,只能注册给app,和app的区别可以添加前缀
    
    @orders.route('/login',methods=['GET',])  #/order/login可以访问,因为前缀,否则根据注册顺序先account的login执行
    def login():
        return 'order 中的 login'
    
    @orders.route('/order',)
    def order():
        return 'order'
        
    -------------------------------------
     #user.py文件
    from flask import Blueprint
    
    users = Blueprint('users', __name__)  # 相当于app一样,知识没有run方法,只能注册给app
    
    @users.route('/user',endpoint='users')
    def user():
        return 'user'
    
  • 在包中的蓝图使用__init__.py进行整合

    from flask import Flask,request
    
    app = Flask(__name__,template_folder='templates'
                ,static_folder='statics',static_url_path='/static') #实例一个能run的app
    
    from views.account import  account #导入所有的模块否则还是无法识别
    from views.user import  users
    from views.order import  orders
    
    app.register_blueprint(account)  #将三个蓝图注册到app中,app.run也就相当于所有蓝图run
    app.register_blueprint(users)
    app.register_blueprint(orders)
    
  • 最后在a2.py文件中run

    from views import app
    
    if __name__ == '__main__':  
        app.run()  #整个项目启动
    
# 同级目录下的蓝图引用
  • # 目录结构:
  • # 代码实现
     #a4.py文件
    from flask import Blueprint
    bp = Blueprint('app01',__name__)
    
    @bp.route('/users')
    def user():
        return 'I am app01'
    
    -----------------------------------
     #a5.py文件
    from flask import Flask
    from a4 import bp
    app = Flask(__name__)
    app.register_blueprint(bp)
    
    @app.route('/login')
    def login():
        return 'login'
    
    @bp.route('/users')  # 默认不执行此视图函数,因为蓝图中已有,可以给蓝图添加url_prefix方法限制,将注册哪一行写下面会报错,因为蓝图始终要注册,相当于要覆盖user
    def user():
        return 'I app'
    
    if __name__ == '__main__':
        app.run()
    
# 实例化蓝图的参数说明
  • 每个蓝图都可以有自己的模板路径、静态文件、以及url_prefix等等

     def __init__(
            self,
            name,
            import_name,
            static_folder=None,
            static_url_path=None,
            template_folder=None,  #蓝图的自己的模板,注意:默认是在同级目录下的模板
            url_prefix=None,   # 自己的url前缀,用于区分不同蓝图的同名函数
            subdomain=None,
            url_defaults=None,
            root_path=None,
            cli_group=_sentinel,
        ):pass
        
     #blog = Blueprint('blog',__name__, template_folder='bb'),会默认去同级目录下找bb模板文件夹
    
# 蓝图的请求扩展
  • 每个蓝图都可以有自己的before_request。请求到来现在app的before_request进行判断,如果是蓝图的路由,就将请求分发下去

     #a4.py文件
    from flask import Blueprint
    bp = Blueprint('app01',__name__)
    
    @bp.before_request
    def before_req(*args, **kwargs):
        print('bp 位置')
    
    @bp.route('/users')
    def user():
        return 'I am app01'
    
    -----------------------------------
     #a5.py文件
    from flask import Flask
    from a4 import bp
    app = Flask(__name__)
    app.register_blueprint(bp)
    
    @app.before_request
    def before_req(*args, **kwargs):
        print('app 位置')
    
    @app.route('/login')
    def login():
        return 'login'
    
    @bp.route('/users')
    def user():
        return 'I app'
    
    if __name__ == '__main__':
        app.run()
        
     #访问/users路径结果:
    app 位置
    bp 位置
    
# 总结蓝图
蓝图用来给Flask构造目录
    1、可以批量分类url
    2、分发模板路径/静态文件路径等
    3、请求扩展(适用于白名单)
# 蓝图构建大型项目
  • 目录结构:

  • 要点讲解:

    创建了两层init函数,最外层用于整合蓝图到app中,内层init用于创建蓝图,以便views中使用
    
     # 外层init
    from flask import Flask
    from .admin import admin
    from .web import web
    
    app = Flask(__name__)
    app.debug = True
    
    app.register_blueprint(admin, url_prefix='/admin')
    app.register_blueprint(web)
    
     # 内层init
    from flask import Blueprint
    admin = Blueprint(
        'admin',
        __name__,
        template_folder='templates',
        static_folder='static'
    )
    from . import views