龙卷风异步装饰器,其中一个依赖于另一个

时间:2014-03-10 15:27:53

标签: asynchronous tornado tornado-motor

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

我想要实现的目标是:

  1. 创建一个使用motor从mongo异步拉取用户记录的装饰器
  2. 验证其凭据(用户名和令牌)
  3. 创建另一个装饰器,检查上面1.中检索到的user_id是否允许访问聊天室。这需要另一个与motor的mongo异步调用来检索'ChatRoom'记录。
  4. 如果一切正常,请订阅聊天室
  5. 我有装饰师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之后,我是否只需要在上述方法中调用验证方法,这样更像回调?

    我希望我的Handler中的方法能够包含这两个装饰器,所以我可以在其他地方重复使用它们,即:

    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()吗?

1 个答案:

答案 0 :(得分:0)

您需要切换装饰器的顺序,以便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:它更清晰。但我把它留作练习。