在所有請求之前添加鉤子
在做驗證是否是登陸用戶之類的功能時买窟,一定會涉及到與此相關(guān)的知識點挽鞠。
對于直接用 app.add_url_rule() 或者使用 @app.route(...) 的方式來定義路由的情況院峡,我們可以使用@app.before_request() 裝飾器來實現(xiàn)在所有請求之前執(zhí)行某一些驗證邏輯代碼咱士。
但是對于用blueprint來定義各種路由的方式影斑,blueprint.before_request 不再是一個裝飾器框仔,而是一個函數(shù)惠遏,并且接受一個參數(shù)砾跃,參數(shù)是一個方法名 func。也就是在執(zhí)行所有請求之前會先執(zhí)行這個 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() 方法帚豪。這四種方法究竟在哪些情況下執(zhí)行碳竟?誰先誰后?這些問題都將直接影響到網(wǎng)站訪問控制的邏輯狸臣,如果理解錯誤莹桅,還可能造成一些訪問的死循環(huán)。
我們將前面的代碼改為:
#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 就跳轉(zhuǎn)到 login 頁面去
我們首先訪問網(wǎng)站根頁面,然后點擊跳轉(zhuǎn)到 stock 頁面此洲,所看到的打印記錄是:
# 這是訪問網(wǎng)站根頁面的反饋
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 -
# 這是跳轉(zhuǎn)到 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 -
從上面的打印結(jié)果來對比厂汗,可以得出如下結(jié)論:
blueprint.before_app_request 會影響全局,即訪問網(wǎng)站根頁面或其他頁面時呜师,它都會生效
blueprint.before_app_request 會最先執(zhí)行娶桦,先于 app.before_request
blueprint.before_app_first_request 會影響全局,但是只執(zhí)行一次汁汗,適合用在初始化某些變量衷畦,它的執(zhí)行順序是在 app.before_request 之后
blueprint.before_app_request 要慎用,假如上面的代碼中將驗證 session 不存在就跳轉(zhuǎn)到 login 頁面的邏輯代碼知牌,寫在了 blueprint_validate_app_session_in_stock() 中祈争,就會造成訪問的死循環(huán)! 因為訪問 login 頁面時角寸,這個驗證的邏輯代碼同樣會生效:發(fā)現(xiàn)沒有 session菩混,又一次跳轉(zhuǎn)到 login 頁面忿墅,造成頁面一直在刷新!
關(guān)于頁面刷新
如果用戶停留在某一頁面沮峡,只是在瀏覽器上點擊了刷新疚脐,會有什么效果呢?我們將上面的代碼中驗證 session 不存在就跳轉(zhuǎn)的 login 頁面的代碼去掉后邢疙,即注釋掉這部分代碼:
#session=None
# if session==None:
# return redirect('/login')
直接訪問 blueprint_stock 的根頁面來查看一下打印結(jié)果:
# 這是第一次訪問的打印結(jié)果
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 -
# 這是用戶點擊刷新后的打印結(jié)果:
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 -
可以看到棍弄,刷新后有兩個變化:
- 執(zhí)行順序變了,刷新后首先執(zhí)行的是 app.before_request
- 刷新后 blueprint.before_app_first_request 不執(zhí)行了疟游,這個很好理解
結(jié)論
- 一定要慎用 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 全局型代碼在開發(fā)管理層面上帶來的不便(假如app層面的代碼和各個 blueprint 由不同的人維護)