如何将RequestHandler子类化为自动验证参数?

时间:2014-04-11 12:16:54

标签: tornado python-3.2

我有以下代码:

class CounterIDHandler(RequestHandler):
    @gen.coroutine
    def get(self, counter_id):
        try:
            object_id = bson.objectid.ObjectId(counter_id)
        except bson.errors.InvalidId as e:
            self.finish(json_encode({'e': str(e)}))
            return
            # I want to finish execution here

class CounterHandler(CounterIDHandler):
    @gen.coroutine
    def get(self, counter_id):
        super().get(counter_id)
        print("this should not print if we get exception in super().get")
        try:
            # I want to use object_id here
        except Exception as e:
            self.finish(json_encode({'e': str(e)}))

这显然不起作用,但它显示了我想要做的事情。 self.finish()终止与客户端的连接,但它不会终止执行。

我想验证counter_id是一个有效的object_id,而不是在所有处理程序中复制粘贴代码。

2 个答案:

答案 0 :(得分:2)

你可以做一个装饰师,像这样(未经测试):

def oid_validator(f):
    @web.asynchronous
    def wrapped(self, oid_str):
        try:
            oid = bson.objectid.ObjectId(oid_str)
        except bson.errors.InvalidId as e:
            self.finish(json_encode({'e': str(e)}))
        else:
            coro = gen.coroutine(f)
            coro(self, oid)

然后,您可以使用get()来装饰@gen.coroutine方法,而不是使用@oid_validator来装饰它们:

class CounterIDHandler(RequestHandler):
    @oid_validator
    def get(self, counter_id):
        # counter_id is now an ObjectId instance

答案 1 :(得分:1)

为什么要将它放在get()基类中?在我看来,这应该采用单独的get_object_id方法。在任何情况下,共享方法有两种方式影响调用者:异常和返回值。

使用返回值None表示调用者应该停止:

class CounterIDHandler(RequestHandler):
    def get_object_id(self, counter_id):
        try:
            return bson.objectid.ObjectId(counter_id)
        except bson.errors.InvalidId as e:
            self.finish(json_encode({'e': str(e)}))
            return None

class CounterHandler(CounterIDHandler):
    def get(self, counter_id):
        object_id = self.get_object_id(counter_id)
        if object_id is None:
            return

或者使用例外和write_error处理程序:

class CounterIDHandler(RequestHandler):
    def get_object_id(self, counter_id):
        return bson.objectid.ObjectId(counter_id)

    def write_error(self, status_code, **kwargs):
        if 'exc_info' in kwargs:
            typ, exc, tb = kwargs['exc_info']
            if isinstance(exc, bson.errors.InvalidId):
                self.finish(json_encode({'e': str(e)}))
                return
        super(CounterIDHandler, self).write_error(status_code, **kwargs)

class CounterHandler(CounterIDHandler):
    def get(self, counter_id):
        object_id = self.get_object_id()