在所有请求之前添加钩子
在做验证是否是登陆用户之类的功能时,一定会涉及到与此相关的知识点。
对于直接用 app.add_url_rule() 或者使用 @app.route(...) 的方式来定义路由的情况,我们可以使用@app.before_request() 装饰器来实现在所有请求之前执行某一些验证逻辑代码。
但是对于用blueprint来定义各种路由的方式,blueprint.before_request 不再是一个装饰器,而是一个函数,并且接受一个参数,参数是一个方法名 func。也就是在执行所有请求之前会先执行这个 func 方法
最基本的代码是:
def blueprint_validate_session_in_stock():
print 'this is [before_request] in blueprint'
def create_app(config_name):
app=Flask(__name__)
app.config.from_object(config.config['base'])
stocks_blueprint.before_request(
blueprint_validate_session_in_stock)
app.register_blueprint(main_blueprint)
app.register_blueprint(stocks_blueprint)
return app
这样的话,每次访问 stocks_blueprint 模块中的页面,都会打印:this is [before_request] in blueprint
一个有益的对比
由于 blueprint 不仅有 before_request() 方法,还有 before_app_request() 方法,还有 before_app_first_request() 方法,再加上在 app 层面还有 app.before_request() 方法。这四种方法究竟在哪些情况下执行?谁先谁后?这些问题都将直接影响到网站访问控制的逻辑,如果理解错误,还可能造成一些访问的死循环。
我们将前面的代码改为:
#encoding:utf8
from flask import Flask,redirect
import config
from controllers import main_blueprint
from controllers.stocks import stocks_blueprint
def validate_session():
print 'over the top! this is before_request\n'
def blueprint_validate_app_session_in_stock():
print '\tthis is [before_app_request] in blueprint\n'
def blueprint_validate_app_session_first_in_stock():
print '\tthis is [before_app_first_request] in blueprint\n'
def blueprint_validate_session_in_stock():
print '\tthis is [before_request] in blueprint\n'
session=None
if session==None:
return redirect('/login')
def create_app(config_name):
app=Flask(__name__)
app.config.from_object(config.config['base'])
app.before_request(validate_session)
stocks_blueprint.before_request(
blueprint_validate_session_in_stock)
stocks_blueprint.before_app_first_request(
blueprint_validate_app_session_first_in_stock
)
stocks_blueprint.before_app_request(
blueprint_validate_app_session_in_stock)
app.register_blueprint(main_blueprint)
app.register_blueprint(stocks_blueprint)
# print app.url_map
return app
代码的意图是:访问 blueprint_stock 的根页面时,要做 session 验证,如果没有 session 就跳转到 login 页面去
我们首先访问网站根页面,然后点击跳转到 stock 页面,所看到的打印记录是:
# 这是访问网站根页面的反馈
this is [before_app_first_request] in blueprint
over the top! this is before_request
this is [before_app_request] in blueprint
127.0.0.1 - - [25/Sep/2017 11:45:59] "GET / HTTP/1.1" 200 -
# 这是访问 stock 根页面的反馈
over the top! this is before_request
this is [before_app_request] in blueprint
this is [before_request] in blueprint
127.0.0.1 - - [25/Sep/2017 11:46:01] "GET /stocks/ HTTP/1.1" 302 -
# 这是跳转到 login 页面的反馈
over the top! this is before_request
this is [before_app_request] in blueprint
127.0.0.1 - - [25/Sep/2017 11:46:01] "GET /login HTTP/1.1" 200 -
从上面的打印结果来对比,可以得出如下结论:
-
blueprint.before_app_request 会影响全局,即访问网站根页面或其他页面时,它都会生效
-
blueprint.before_app_request 会最先执行,先于 app.before_request
-
blueprint.before_app_first_request 会影响全局,但是只执行一次,适合用在初始化某些变量,它的执行顺序是在 app.before_request 之后
-
blueprint.before_app_request 要慎用,假如上面的代码中将验证 session 不存在就跳转到 login 页面的逻辑代码,写在了 blueprint_validate_app_session_in_stock() 中,就会造成访问的死循环! 因为访问 login 页面时,这个验证的逻辑代码同样会生效:发现没有 session,又一次跳转到 login 页面,造成页面一直在刷新!
关于页面刷新
如果用户停留在某一页面,只是在浏览器上点击了刷新,会有什么效果呢?我们将上面的代码中验证 session 不存在就跳转的 login 页面的代码去掉后,即注释掉这部分代码:
#session=None
# if session==None:
# return redirect('/login')
直接访问 blueprint_stock 的根页面来查看一下打印结果:
# 这是第一次访问的打印结果
this is [before_app_first_request] in blueprint
over the top! this is before_request
this is [before_app_request] in blueprint
this is [before_request] in blueprint
127.0.0.1 - - [25/Sep/2017 11:56:25] "GET /stocks/ HTTP/1.1" 200 -
# 这是用户点击刷新后的打印结果:
over the top! this is before_request
this is [before_app_request] in blueprint
this is [before_request] in blueprint
127.0.0.1 - - [25/Sep/2017 11:56:48] "GET /stocks/ HTTP/1.1" 200 -
可以看到,刷新后有两个变化:
- 执行顺序变了,刷新后首先执行的是 app.before_request
- 刷新后 blueprint.before_app_first_request 不执行了,这个很好理解
结论
- 一定要慎用 blueprint.before_app_request
- blueprint 层面没有 before_first_request,如果要使用 blueprint.before_app_first_request 等同于 app.before_first_request 了,这个也要慎用。
- 对于 blueprint.before_app_first_request 的应用场景是在各个 blueprint 中可以单独添加一些自己需要初始化的变量,各个 blueprint 中的这个操作相互是不会覆盖的,只是在 app 层面的 before_first_request 中做“加操作”,避免了直接修改 app.before_first_request 全局型代码在开发管理层面上带来的不便(假如app层面的代码和各个 blueprint 由不同的人维护)
网友评论