我有一个使用flask-restx
和flask-login
的Flask应用程序。我希望默认情况下所有路由都需要登录,并明确定义不需要身份验证的公共路由。我已经按照这个问题中给出的例子开始使用装饰器:
使烧瓶登录的最佳方式';s login_需要默认
它适用于函数端点,但不适用于restx
资源端点。
我尝试将函数添加为decorator,并使用method_decorators
字段。例如:
def public_route(decorated_function):
"""
This is a decorator to specify public endpoints in our flask routes
:param decorated_function:
:return:
"""
decorated_function.is_public = True
return decorated_function
class HelloWorld(ConfigurableResource):
method_decorators = {"get": [public_route]}
@public_route
@api.doc('Welcome message')
def get(self):
return {'hello': 'world'}
这个测试通过了:
def test_hello_world_is_public():
api = Namespace('health', description='Health related operations')
hello = HelloWorld(api, config=None, logger=None)
is_public_endpoint = getattr(hello.get, 'is_public', False)
assert is_public_endpoint
我的挑战是,我看不出如何在我的身份验证逻辑中访问这个属性:
@app.before_request
def check_route_access():
"""
This function decides whethere access should be granted to an endpoint.
This function runs before all requests.
:return:
"""
is_public_endpoint = getattr(app.view_functions[request.endpoint], 'is_public', False)
if person_authorized_for_path(current_user, request.path, is_public_endpoint):
return
# Otherwise access not granted
return redirect(url_for("auth.index"))
这适用于普通函数端点,但不适用于restx资源。
我知道restx
将我的资源类封装在一个函数中,这样flask就可以进行调度,但我不知道如何从这里访问decorator。所以我有一些问题:
- 是否可以从view_function访问decorator
- 是否可以知道端点是restx资源还是纯rest函数
- 有没有更好的方法来实现我想要实现的目标
基于这个和这个,method_decorators
变量应该是一个函数列表,所以你应该像一样使用它
def _perform_auth(method):
is_public_endpoint = getattr(method, 'is_public', False)
# place the validation here
class Resource(flask_restx.Resource):
method_decorators = [_perform_auth]
是否可以从view_function访问装饰器?
嗯。。。这是可能,但我不推荐它。下面是一个示例
是否可以知道端点是restx资源还是普通休息功能?
您可能可以检查func并确定它是否来自restx,也许可以查看__qualname__
,但我不建议使用它。
有更好的方法来实现我想要实现的目标吗?
我会选择其中一种解决方案:
- 显式修饰view_funcs和确实需要身份验证的资源,而不是相反
- 创建公共端点的蓝图,使用
before_request
装饰器进行授权的受保护端点的蓝图
我有一个类似的用例。我想通过使用flask restx默认装饰器将装饰器应用于特定的端点(这里是除get方法之外的所有路由(。
这是我的解决方案。
def check_role_except_get(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
if func.__name__ == "get"
return func(*args,**kwargs)
else:
........ Authorization Logic Here / Abort if Authorization fails----
return wrapper
def authorization(func):
@functools.wraps(func)
def wrapper(*args,**kwargs):
func.view_class.method_decorators = [check_role_except_get]
return func(*args,**kwargs)
return wrapper
api = Api(app, version='1.0', title='My API',
description='A simple API',decorators=[authorization]
)
ns = api.namespace('todos', description='TODO operations')
@ns.route('/')
class TodoList(Resource):
'''Shows a list of all todos, and lets you POST to add new tasks'''
def get(self):
'''List all tasks'''
return "resp"
def post(self):
'''Create a new task'''
return resp