我将把它分成,1)为什么你的代码会失败,2)一个糟糕的黑客解决方案应该解决你的直接问题以及为什么它会产生未来的问题,3)一种不同的方法来划分责任,但可能行不通因为我不知道你的设计细节。
1 - 实际上,您的装饰器获取current_usuario 的值并将其绑定到您调用的方法的第一个参数。当该方法被调用时,所有其他参数都会在之后被分配。这是一个更清晰的例子。
from functools import wraps
def assign_first(func):
@wraps(func)
def wrapper(*args, **kwargs):
# do stuff
first_argument = 10
return func(first_argument, *args, **kwargs)
return wrapper
class MyClass(object):
@assign_first
def first_method(self, first, second):
print(f"self: {self}")
print(f"first: {first}")
print(f"second: {second}")
输出:
>>> MyClass().first_method(100)
self: 10
first: <__main__.MyClass object at 0x7fdbdebd3400>
second: 100
装饰器将10绑定到方法的第一个参数(self),因此实例(应该绑定到self)现在绑定到第二个参数(称为first) .您收到该错误消息是因为您的 UserView 对象正在绑定到 current_usuario,并且没有 admin 属性。
2) 在你的情况下,你可以做一个非常错误的想法,让你的包装器绑定到一个关键字参数。但是你需要所有修饰的方法都知道它们需要特定的关键字参数并且只使用关键字参数。
def assign_my_kwarg(func):
@wraps(func)
def new_func(*args, **kwargs):
# do stuff
return func(*args, my_kwarg=3, **kwargs)
return new_func
class MyClass(object):
@assign_my_kwarg
def second_method(self, *, my_kwarg, second=1): # a function that only takes keyword arguments
print(f"self: {self}")
print(f"my_kwarg: {my_kwarg}")
print(f"second: {second}")
运行:
>>> MyClass().second_method(second=5)
self: <__main__.MyClass object at 0x7f679eccd400>
my_kwarg: 3
second: 5
所以你可以用你的token_required 做同样的事情,返回return f(*args, current_usuario=current_usuario, **kwargs) 并将你的方法签名更改为:def get(self, *, current_usuario, usuario_id = None):。但它会非常脆弱并且非常混乱。任何调用该方法并输入current_usuario 的人都会导致错误,因为它已经被分配了。关键字的任何拼写错误都会导致错误。更改关键字的名称会令人头疼。
3) 真正的问题是token_required 同时检查令牌和 获取当前用户。您可以通过使用一个函数来获取引发自定义令牌错误的当前用户,然后使用一个装饰器来处理这些错误,从而避开这一点。
from functools import wraps
class BadTokenError(ValueError):
pass
class MissingTokenError(ValueError):
pass
def get_current_user():
token = get_token()
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
return Usuarios.query.filter_by(usuarioid=data['usuarioid']).first()
except (SPECIFIC_ERROR, OTHER_SPECIFIC_ERROR): # do not catch MissingTokenError
msg = 'bad token'
raise BadTokenError(msg)
def get_token():
try:
token_name = 'x-access-token'
return request.headers[token_name]
except KeyError:
msg = 'missing token'
raise MissingTokenError(msg)
def catch_token_errors(func):
@wraps(func)
def func_with_handler(*args, **kwargs):
try:
return func(*args, **kwargs)
except (BadTokenError, MissingTokenError) as error:
return jsonify({'message': error.args[0]}), 401
return func_with_handler
class UsuariosView(MethodView):
@catch_token_errors
def get(self, usuario_id=None):
current_usuario = get_current_user()
if not current_usuario.admin:
return ({'message': 'Not admin!'})
这会进一步分散职责,但可能无法满足您的需要。