官方文档
To use the decorator, apply it as innermost decorator to a view function. When applying further decorators, always remember that the route() decorator is the outermost.
装饰的顺序按靠近函数顺序执行,从内到外装饰,调用时由外而内,执行顺序和装饰顺序相反。
Flask
Flask 框架中, route 装饰器是这么写的:
def route(self, rule, **options):
“”“Like :meth:Flask.route
but for a blueprint. The endpoint for the
:func:url_for
function is prefixed with the name of the blueprint.
“””
def decorator(f):
endpoint = options.pop(“endpoint”, f.name)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
route 调用了 add_url_rule , 对传入的 f 添加一条 URL 规则。
所以,按照 python 装饰器顺序:
- 如果 @app.route 在内层,那么就会把最原始的 view 函数传给 add_url_rule , Flask 框架就会添加一条 URL 规则,指向最原始的 view 函数。
- 如果 @app.route 在外层,那么就会把已经被 login_required 装饰过的 view 函数传给 add_url_rule , Flask 框架就会添加一条 URL 规则,指向已经装饰过的 view 函数。
正确写法
@user_blueprint.route('/admin/refresh_session/', methods=['POST'])
@login_required
def refresh_session():
pass
这段代码相当于:
# 这里没有装饰器
def refresh_session():
pass
login_wrapped = login_required(refresh_session) # login 装饰器
both_wrapped = app.route('/admin/refresh_session/')(login_wrapped) # route 装饰器
/admin/refresh_session/ 这条路由指向的实际是 login_wrapped ,这样就会经过 login 检查
错误写法
@login_required
@user_blueprint.route('/admin/refresh_session/', methods=['POST'])
def refresh_session():
pass
这段代码相当于:
# 这里没有装饰器
def refresh_session():
pass
route_wrapped = app.route('/admin/refresh_session/')(refresh_session) # route 装饰器
login_wrapped = login_required(route_wrapped) # login 装饰器
/admin/refresh_session/ 这条路由指向的实际是 refresh_session , 而 login_wrapped 并没有与路由挂勾,所以不会被调用