Как создать подкласс RequestHandler для автоматической проверки аргументов?

У меня есть следующий код:

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 без копирования и вставки кода во всех обработчиках.


person ragezor    schedule 11.04.2014    source источник


Ответы (2)


Почему вы помещаете это в 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()
person Ben Darnell    schedule 11.04.2014

Вы можете сделать декоратор, что-то вроде этого (непроверенный):

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
person A. Jesse Jiryu Davis    schedule 11.04.2014
comment
Действительно классное решение, я думаю, что оно очень общее, и вы можете использовать его каждый раз, когда вам нужен валидатор, но в этом случае мне больше нравится решение Бена. - person ragezor; 11.04.2014