因此,我试图将一个对象从装饰器返回到类的方法。基本上,我正在使用装饰器方法的API:
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = None
if 'x-access-token' in request.headers:
token = request.headers['x-access-token']
if not token:
return jsonify({'message' : 'Token is missing!'}), 401
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
current_usuario = Usuarios.query.filter_by(usuarioid = data['usuarioid']).first()
except:
return jsonify({'message' : 'Token is invalid!'}), 401
return f(current_usuario, *args, **kwargs)
return decorated
问题是我需要在UsuariosView类的方法中返回current_usuario,这是我现在正在尝试的方法:
class UsuariosView(MethodView):
@token_required
def get(self, current_usuario, usuario_id = None):
if not current_usuario.admin:
return ({'message' : 'Not admin!'})
#do stuff
我没有找回current_usuario,所以我无法访问他的属性admin并得到以下错误:
AttributeError:'UsuariosView'对象没有属性'admin'
答案 0 :(得分:0)
我将其分为以下几类: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!'})
这可以使职责分得更多,但可能无法满足您的需求。