Как да подкласирам 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