Flask 中 before_request, after_request, errorhandler 和 teardown_request 執行順序

Flask 中有幾個處理 request 的裝飾器, 分別爲 before_request, after_request, errorhandlerteardown_request. 簡單的來講對應爲請求前執行, 請求正確執行後執行, 發生錯誤時執行, 返回 response 前執行.python

before_request

before_request 很好理解, 就是在處理路由規則對應的 view_function 以前執行的函數, 而且執行順序是先綁定先執行, 而且先執行 flask appbefore_request, 再處理 blueprintbefore_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

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

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 就和其他的三個不太同樣了. 嚴格的來講 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 出請求棧的時候執行. 如圖所示: 路由

執行順序
相關文章
相關標籤/搜索