Flask 中有幾個處理 request 的裝飾器, 分別爲 before_request
, after_request
, errorhandler
和 teardown_request
. 簡單的來講對應爲請求前執行, 請求正確執行後執行, 發生錯誤時執行, 返回 response 前執行.python
before_request
很好理解, 就是在處理路由規則對應的 view_function
以前執行的函數, 而且執行順序是先綁定先執行, 而且先執行 flask app
的 before_request
, 再處理 blueprint
的 before_request
. 好比:flask
@app.before_request
def before_request_a():
print('I am in before_request_a')
@app.before_request
def before_request_b():
print('I am in before_request_b')
# 打印結果 -=-=-=-=-=-=-=-=-=-=-=-=-=
I am in before_request_a
I am in before_request_b
複製代碼
可見源碼:app
def preprocess_request(self):
"""Called before the request is dispatched. Calls :attr:`url_value_preprocessors` registered with the app and the current blueprint (if any). Then calls :attr:`before_request_funcs` registered with the app and the blueprint. If any :meth:`before_request` handler returns a non-None value, the value is handled as if it was the return value from the view, and further request handling is stopped. """
bp = _request_ctx_stack.top.request.blueprint
# 直接 register 在 flask app 上的 before_request
funcs = self.before_request_funcs.get(None, ())
# register 在 blueprint 上的 before_request
if bp is not None and bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
for func in funcs:
rv = func()
# 若是 before_request 有非空的返回, 都會直接做爲 response 返回
if rv is not None:
return rv
複製代碼
errorhandler
被觸發的前提是 view_function
中拋出了錯誤, 而且錯誤碼可以匹配上註冊的 errorhandler
的錯誤碼. 好比:函數
@app.errorhandler(500)
def error_handler(err):
print('I am in error_handler')
raise err
# 打印結果 -=-=-=-=-=-=-=-=-=-=-=-=-=
I am in error_handler
複製代碼
after_request
被觸發的前提是沒有異常拋出; 或者異常被 errorhandler
接住並處理. 而且 after_request
執行的順序是先綁定後執行. 好比:url
@app.after_request
def after_request_a(response):
print('I am in after_request_a')
return response
@app.after_request
def after_request_b(response):
print('I am in after_request_b')
return response
複製代碼
方式一: errorhandler 可以 handle 拋出的異常spa
@app.errorhandler(500)
def error_handler(err):
print('I am in error_handler')
return 'ERROR PAGE'
# 打印結果 -=-=-=-=-=-=-=-=-=-=-=-=-=
I am in error_handler
I am in after_request_b
I am in after_request_a
複製代碼
方式二: errorhandler 沒法 handle 拋出的異常code
# 若是將 error_handler改成
@app.errorhandler(500)
def error_handler(err):
print('I am in error_handler')
raise err
# 打印結果 -=-=-=-=-=-=-=-=-=-=-=-=-=
I am in error_handler
複製代碼
teardown_request
就和其他的三個不太同樣了. 嚴格的來講 teardown_request
沒有固定的執行位置. 由於他直接和請求上下文環境掛鉤. 只有在請求上下文被 pop
出請求棧的時候纔會觸發 teardown_request
, 因此即便以前有拋出錯誤的時候也會都會被執行, 執行完後返回 response.cdn
# flask/ctx.py
class RequestContext(object):
...
def pop(self, exc=_sentinel):
...
self.app.do_teardown_request(exc)
...
# flask/app.py
class Flask:
...
def do_teardown_request(self, exc=_sentinel):
...
funcs = reversed(self.teardown_request_funcs.get(None, ()))
bp = _request_ctx_stack.top.request.blueprint
if bp is not None and bp in self.teardown_request_funcs:
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
for func in funcs:
func(exc)
...
複製代碼
而且執行 teardown_request
的時候也是先綁定的後執行. 好比:blog
@app.teardown_request
def teardown_request_a(exc):
print('I am in teardown_request_a')
@app.teardown_request
def teardown_request_b(exc):
print('I am in teardown_request_b')
# 打印結果 -=-=-=-=-=-=-=-=-=-=-=-=-=
I am in teardown_request_a
I am in teardown_request_b
複製代碼
因此總的來講, 這幾個裝飾器裝飾的方法執行的前後爲 before_request
-> errorhandler
-> after_request
, teardown_request
在將當前請求 pop
出請求棧的時候執行. 如圖所示: 路由