【问题标题】:Tornado async decorators where one depends on anotherTornado 异步装饰器,其中一个依赖于另一个
【发布时间】:2014-03-10 15:27:53
【问题描述】:

我正试图绕过 Tornado。我正在编写一个由 mongodb 支持的聊天应用程序,我正在使用 motor 对其进行非阻塞访问。

我想要实现的是:

  1. 创建一个使用motor从mongo异步拉取用户记录的装饰器
  2. 验证他们的凭据(用户名和令牌)
  3. 创建另一个装饰器,检查上面 1. 中检索到的 user_id 是否被允许访问聊天室。这需要使用 motor 再次异步调用 mongo 以检索“ChatRoom”记录。
  4. 如果一切正常,请订阅聊天室

我有装饰器 1. 工作(基本上取自 http://tornadogists.org/5251927/):

def authenticated_async(f):
    @functools.wraps(f)
    @gen.engine
    def wrapper(self, *args, **kwargs):
        self.current_user = yield gen.Task(self.get_current_user_async)
        if self.current_user:
            logging.info("User '%s' (%s) successfully authenticated" %
                         (self.current_user['username'],
                          self.current_user['_id']))
            f(self, *args, **kwargs)
        else:
            raise tornado.web.HTTPError(401, "User not authenticated, "
                                             "aborting")
    return wrapper

麻烦的是,对于第二个装饰器,我需要访问self.current_user。因为这是在异步回调中设置的,所以当我进入我的 validation 装饰器时它不可用(即在 auth 装饰器完成之前调用验证装饰器)。

我不可能以这种方式将装饰器与异步函数一起使用吗?在确保self.current_user为True之后,我是否只需要在上述方法中调用验证方法,这样它更像是一个回调?

理想情况下,我希望我的处理程序中的方法与这两个装饰器一起包装,以便我可以在其他地方重用它们,即:

class ChatSocketHandler(tornado.websocket.WebSocketHandler):
    @gen.coroutine
    @validate_invitation_access_async
    @authenticated_async
    def open(self, invitation_id):
        # do stuff here...

更新 事实上,没有依赖关系。 user_id 作为参数提供,可用于并行运行两个装饰器 - 一个用于确认身份验证,另一个用于查看是否允许具有该 ID 的用户访问房间。 open() 方法只有在 self.auth_check == True and self.room_check == True 时才会继续。

可以在异步装饰器完成之前调用open() 吗?

【问题讨论】:

    标签: asynchronous tornado tornado-motor


    【解决方案1】:

    您需要切换装饰器的顺序,以便您的 validate_invitation_access_async 包装器可以访问 current_user:

    @authenticated_async
    @validate_invitation_access_async
    def open(self, invitation_id):
            # do stuff here...
    

    那么validate_invitation_access_async里面的包装就是authenticated_async里面的“f”:它在self.current_user设置后被调用。请注意,您不需要额外的 gen.engine 装饰器,所有包装器都已经是引擎。你的包装可能是这样的:

    def validate_invitation_access_async(f):
        @gen.engine
        def wrapper(self, *args, **kwargs):
            # ... use Motor to see if user is authorized ...
            # if not, log and redirect. otherwise:
            f(self, *args, **kwargs)
    

    您还应该更新您的代码以使用 Tornado 3 的 gen.coroutine 而不是 gen.engine:它更干净。但我把它留作练习。

    【讨论】:

    • 谢谢,它有效,但我一直认为装饰器是从最里面到最外面执行的,如下所述:thadeusb.com/weblog/2010/8/23/python_multiple_decorators。为什么你的工作?
    • 因为那个帖子是错误的。如果装饰器像我们讨论的那样编写,则首先输入顶部的,最后输入底部的。当最里面的函数返回时,返回值从下到上传递回装饰器堆栈。但在我们的例子中,我们没有返回任何东西,所以重要的是进入阶段,而不是退出阶段。
    猜你喜欢
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 2018-04-11
    • 2013-01-05
    • 2017-11-16
    • 2020-10-30
    • 2011-09-03
    • 1970-01-01
    相关资源
    最近更新 更多