Как обработать 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
ДИКТ.