在规则映射到端点之前如何在 Flask 中处理 URL?
How to process URLs in Flask before Rules are mapped to endpoints?
我正在尝试在 API 中实现一项功能,其中 URL 的一部分是可选的。如果提供,我想处理它并在 g 中粘贴一些数据。如果没有,我会在 g 中放置一些默认信息。无论哪种方式,我都会在规则映射到端点之前将其从 URL 中删除。所以我希望以下两个 URLs 最终调用相同的端点:
/bar/1 (I would fill in a default value for foo here)
/foo/32/bar/1
我想要在我拥有的每个端点中使用相同的 URL 可选部分。我想我可以通过装饰每个端点来蛮力做到这一点,但我有超过 250 个端点,所以我想要更优雅的东西。
我正在使用多个蓝图,如果可能的话,我想让每个端点尽可能简单(蓝图已经有自己的前缀):
@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 的挂钩?
通常在应用程序初始化期间,URL 仅映射到端点一次。更准确地说 - 每次解释器第一次遇到代码 @app.route('/some_route') ...
时。重要的是要了解每个请求都不会将 URL 映射到端点(例如 PHP)。
向端点添加一些默认值的一种方法是覆盖
Flask.route()
Flask.add_url_rule()
Blueprint.route()
您的继任者 app/blueprint class。只需将其放入 **options
dict.
我通过子classing Flask class 并覆盖 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( ... )
我正在尝试在 API 中实现一项功能,其中 URL 的一部分是可选的。如果提供,我想处理它并在 g 中粘贴一些数据。如果没有,我会在 g 中放置一些默认信息。无论哪种方式,我都会在规则映射到端点之前将其从 URL 中删除。所以我希望以下两个 URLs 最终调用相同的端点:
/bar/1 (I would fill in a default value for foo here)
/foo/32/bar/1
我想要在我拥有的每个端点中使用相同的 URL 可选部分。我想我可以通过装饰每个端点来蛮力做到这一点,但我有超过 250 个端点,所以我想要更优雅的东西。
我正在使用多个蓝图,如果可能的话,我想让每个端点尽可能简单(蓝图已经有自己的前缀):
@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 的挂钩?
通常在应用程序初始化期间,URL 仅映射到端点一次。更准确地说 - 每次解释器第一次遇到代码 @app.route('/some_route') ...
时。重要的是要了解每个请求都不会将 URL 映射到端点(例如 PHP)。
向端点添加一些默认值的一种方法是覆盖
Flask.route()
Flask.add_url_rule()
Blueprint.route()
您的继任者 app/blueprint class。只需将其放入 **options
dict.
我通过子classing Flask class 并覆盖 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( ... )