[英]what's the correct way to find out flask's endpoint by function name?
I have a flask demo, and it needs access control. 我有一个烧瓶演示,它需要访问控制。 My access control is base on function name, and I wrote a decorator to do it. 我的访问控制基于函数名,我编写了一个装饰器来实现。
The decorator define is: 装饰器的定义是:
def permission(fn):
@wraps(fn)
def wraped(*args, **kwargs):
if _do_validate(fn.__name__): # TODO do check user has privileges here
return fn(*args, **kwargs)
else:
abort(401)
return wraped
Usage example as follow: 用法示例如下:
@app.route('/index')
@permission
def index():
return 'hello world'
This work fine if user don't specify a endpoint, because Flask's default endpoint is fn.__name__
, in my example the endpoint='index'
. 如果用户未指定端点,则此方法可以正常工作,因为Flask的默认端点为fn.__name__
,在我的示例中为endpoint='index'
。
But when user specify a endpoint, or just use the blueprint, the endpoint was changed. 但是,当用户指定端点或仅使用蓝图时,端点已更改。 For example: 例如:
bl = Blueprint('admin', __name__)
@bl.route('hello')
@permission
def hello():
return 'hello world in blueprint'
The endpoint is changed to admin.hello
. 端点更改为admin.hello
。
I don't want to specify any arg in @permission
, so a I write a new permission decorator as follow: 我不想在@permission
指定任何arg,所以我写了一个新的权限装饰器,如下所示:
def permission(fn):
@wraps(fn)
def wraped(*args, **kwargs):
m = fn.__module__.split('.')
if len(m) <= 2:
# app must define in the root module, so if user not use blueprint, juse use fn.__name__ as endpoint
reg_f = fn.__name__
else:
# blue print must define in the submodule
reg_f = '{0}.{1}'.format(m[1], fn.__name__)
if _do_validate(ref_f): # TODO do check user has privileges here
return fn(*args, **kwargs)
else:
abort(401)
return wraped
The problem is solved, but I think it's not elegant. 问题已解决,但我认为这并不优雅。
Can anyone give me a better one? 谁能给我更好的一个? thx. 谢谢。
Your solution is not valid in case of blueprint's name is not equal to module's __name__
. 如果蓝图的名称不等于模块的__name__
则您的解决方案无效。
I can propose the next solution: 我可以提出下一个解决方案:
from collections import defaultdict
registered = defaultdict(int)
def permission(fn):
registered[fn.__name__] += 1
fn.permission_token = "%s.%s" % (fn.__name__, registered[fn.__name__])
@wraps(fn)
def wraped(*args, **kwargs):
if _do_validate(fn.permission_token):
return fn(*args, **kwargs)
else:
abort(401)
return wraped
@permission
def foo():
pass
@permission
def bar():
pass
def _do_validate(permission_token):
return {foo.permission_token: True,
bar.permission_token: False}.get(permission_token, False)
The main drawback of it is that you have to import all your routes in the _do_validate
's module to create such "access control list". 它的主要缺点是您必须在_do_validate
的模块中导入所有路由,才能创建此类“访问控制列表”。
UPD: Ok, because of you want to use endpoint values to check user access, the next solution allows to find endpoint by view function: UPD:好的,由于您要使用端点值来检查用户访问权限,因此下一个解决方案允许按视图功能查找端点:
from collections import defaultdict
from flask import current_app
def find_endpoint(fn):
# This loop can be optimized by maintaining
# {permission_token => endpoint} dict.
for endpoint, view_func in current_app.view_functions.items():
if getattr(view_func, 'permission_token', None) == fn.permission_token:
return endpoint
return None
registered = defaultdict(int)
def permission(fn):
registered[fn.__name__] += 1
@wraps(fn)
def wrapped(*args, **kwargs):
endpoint = find_endpoint(wrapped)
assert endpoint is not None
if _do_validate(endpoint):
return fn(*args, **kwargs)
else:
abort(401)
# This will work because flask's route decorator returns
# an original view function, not the wrapped one.
wrapped.permission_token = "%s.%s" % (fn.__name__, registered[fn.__name__])
return wrapped
def _do_validate(endpoint):
# Or search in the DB
return {'index': True,
'bp.index': False}.get(endpoint, False)
However, checking access by endpoint is not so good idea, because any code change during a development process could lead to undesired losing (or even worse - retrieving!) an access. 但是,按端点检查访问权限并不是一个好主意,因为在开发过程中任何代码更改都可能导致不希望的丢失(或更糟糕的是,正在获取!)访问权限。 So, for now my opinion is that using an extra action
(or resource
) argument for the permission
decorator will be reasonable. 因此,就目前而言,我认为对permission
装饰器使用额外的action
(或resource
)参数将是合理的。 With such parameter you can freely modify your endpoints/view functions without any undesired side effects. 使用此类参数,您可以自由修改端点/视图功能,而不会产生任何不希望的副作用。
UPD2: Actually, the much more simple way to find an endpoint value into a wrapper function is just use request.endpoint
. UPD2:实际上,在包装函数中查找端点值的更简单方法是使用request.endpoint
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.