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 出请求栈的时候执行. 如图所示: 路由

执行顺序
相关文章
相关标签/搜索