Как обработать URL-адреса во Flask до сопоставления правил с конечными точками?

Я пытаюсь реализовать функцию в моем API, где часть URL является необязательной. Если он будет предоставлен, я бы хотел обработать его и вставить некоторые данные в g. Если нет, я бы поставил некоторую информацию по умолчанию в g. В любом случае, я бы удалил его из URL до того, как правила будут сопоставлены с конечными точками. Поэтому я хотел бы, чтобы следующие два URL-адреса вызывали одну и ту же конечную точку:

/bar/1   (I would fill in a default value for foo here)
/foo/32/bar/1

Я хочу этот же необязательный фрагмент URL в каждой конечной точке, которую я имею. Я думаю, что я мог бы сделать это грубой силой, украсив каждую конечную точку, но у меня их более 250, поэтому я хотел бы что-то более элегантное.

Я использую несколько Blueprints, и я хотел бы оставить каждую конечную точку настолько простой, насколько она у меня уже есть, если это возможно (чертежи уже имеют свои собственные префиксы):

@blueprint1.route('/bar/<int:id>', methods=['GET'])
@blueprint2.route('/bar/<int:id>', methods=['GET'])
def get_foo():

Я пробовал декораторы @url_defaults, @url_value_preprocessor и @before_request, но, похоже, к этому моменту правило уже сопоставлено с конечной точкой. Есть ли ловушка для доступа к URL до того, как будет выполнено сопоставление?

2 ответа

Решение

Я сделал эту работу, создав подкласс класса Flask и переопределив функцию create_url_adapter() следующим образом:

class MyFlask(Flask):
    """
    MyFlask subclasses the standard Flask app class so that I can hook the URL parsing before the URL
    is mapped to a Route. We do this so we can extract an optional "/foo/<int:foo_id>" prefix. If we
    see this prefix, we store the int value for later processing and delete the prefix from the URL before
    Flask maps it to a route.
    """

def _extract_optional_foo_id(self, path):
    ....
    return path, foo_id  # path has the optional part removed

def create_url_adapter(self, request):
        """
        Augment the base class's implementation of create_url_adapter() by extracting an optional foo_id and modifying
        the URL. The Flask function name is misleading: we aren't creating anything like an object. The "adapter" is
        just this function call. We add our own behavior then call the super class's version of this function.
        :param request: Flask's Request object
        :return: the results of the Flask super class's create_url_adapter()
        """
        # Must test for request. It is None when calling this for the app-wide scenario (instead of request-wide).
        if request and request.environ:
            (new_path, foo_id) = self._extract_optional_foo_id(request.environ['PATH_INFO'])
            if foo_id is not None:
                request.environ['PATH_INFO'] = new_path
                request.foo_id_in_url = foo_id

        return super(MyFlask, self).create_url_adapter(request)

Затем в своем коде инициализации приложения вместо создания экземпляра Flask я делаю:

app = MyFlask( ... )

Обычно URL-адреса сопоставляются с конечными точками только один раз во время инициализации приложения. Точнее - каждый раз код @app.route('/some_route') ... встречается с переводчиком впервые. Важно понимать, что сопоставление URL-адресов с конечными точками происходит не для каждого запроса (как, например, в PHP).

Один из способов добавить некоторые значения по умолчанию в конечную точку - переопределить одно из

Flask.route()
Flask.add_url_rule()
Blueprint.route()

в вашем преемнике app/blueprint class. Просто поместите это в **options ДИКТ.

Другие вопросы по тегам