假设,我有一个手工制作的 @login-required
装饰器:
from functools import wraps
def login_required(decorated_function):
"""Decorator to check if user is logged in."""
@wraps(decorated_function)
def wrapper(*args, **kwargs):
if False: # just to check it's working
return decorated_function(*args, **kwargs)
else:
flash('You need to login, to access this page')
return redirect(url_for('login'))
return wrapper
和一个函数,用 @app.route()
和 @login_required
装饰(为简洁起见,省略了 login
的端点):
@app.route('/')
@login_required
def index():
return "Hello!"
现在,如果我尝试访问 /
,正如预期的那样,它不会让我访问,并且会重定向到登录页面。
不过,如果我滑动装饰器的顺序,即:
@login_required
@app.route('/')
def index():
return "Hello!"
然后我就可以访问 /
,即使我不应该这样做。
我知道Flask documentation on the subject状态:
<小时/>When applying further decorators, always remember that the route() decorator is the outermost.
我好奇的不是正确的方法是什么(@app.route()
装饰器必须在最外面 - 明白了),但是而是为什么它会这样工作(即它背后的机制是什么)。
我查看了@app.route()
source code :
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
This answer ,或多或少帮助我理解了装饰器的机制。不过,我以前从未见过函数只是返回(没有调用它),所以我自己做了一个小实验(当然,结果是可行的):
def my_decorator():
def decorator (function):
return function
return decorator
@my_decorator()
def test():
print('Hi')
test()
所以,我想了解:
- 为什么装饰器的顺序在上面的具体情况下以及对于
@app.route()
和其他装饰器来说很重要(我猜这是相同的答案)?让我困惑的是,@app.route()
只是向应用程序添加了 url 规则(即self.add_url_rule(rule, endpoint, f, **options)
和返回函数,就是这样,为什么顺序很重要? @app.route()
是否会覆盖其上方的所有装饰器(如果是的话,如何覆盖)?
我也是aware ,装饰器应用顺序是从下到上,尽管对我来说这并没有让事情变得更清晰。我错过了什么?
最佳答案
你自己已经差不多解释清楚了! :-) app.route
确实
self.add_url_rule(rule, endpoint, f, **options)
但关键是这里的 f
是修饰的任何函数。如果您首先应用 app.route
,它会为原始函数添加 URL 规则(不带登录装饰器)。登录装饰器包装了该函数,但 app.route
已经存储了原始未包装的版本,因此包装无效。
设想“展开”装饰器可能会有所帮助。想象一下你是这样做的:
# plain function
def index():
return "Hello!"
login_wrapped = login_required(index) # login decorator
both_wrapped = app.route('/')(login_wrapped) # route decorator
这是“正确”的方式,首先发生登录换行,然后发生路由。在此版本中,app.route
看到的函数已使用登录包装器进行了包装。错误的做法是:
# plain function
def index():
return "Hello!"
route_wrapped = app.route('/')(index) # route decorator
both_wrapped = login_wrapped(route_wrapped) # login decorator
在这里你可以看到 app.route
看到的只是普通的未包装版本。该函数稍后用登录装饰器包装的事实没有任何影响,因为那时路由装饰器已经完成。
关于python - Flask:为什么 app.route() 装饰器应该总是在最外面?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47467658/