【问题标题】:How to not add prefix to invalid route path如何不向无效路由路径添加前缀
【发布时间】:2021-03-17 14:03:57
【问题描述】:

我正在开发一个 Flask GAE 应用程序,并计划使用 flask-babel 支持多种语言。

我用两个字母的语言代码为所有路由路径添加前缀,并且我设置了重定向来强制执行它。假设英语是用户对我支持的语言的偏好,我将其设置为 / 重定向到 /en//about 重定向到 /en/about/,例如。

我的问题是,这会导致无效路由在 404 之前被重定向。例如,/dsdf 重定向到 /en/dsdf/,然后是 404s。我宁愿只使用没有重定向的纯 URL 404。我不知道如何检查缺少前缀的给定路径是否对应于有效路由(如果它有前缀)。

这是我的main.py 的最小代码示例。我能在网上找到的唯一信息是https://medium.com/@nicolas_84494/flask-create-a-multilingual-web-application-with-language-specific-urls-5d994344f5fd,但对我来说似乎并没有以同样的方式工作; request.url_rule.defaults 评估为 None

from flask import Flask, g, request
from flask_babel import Babel

SUPPORTED_LANGUAGES = ['en', 'es', 'zh']

app = Flask(__name__)
babel = Babel(app)

@babel.localeselector
def get_locale():
    if not g.get('lang', None) or g.lang not in SUPPORTED_LANGUAGES:
        g.lang = request.accept_languages.best_match(SUPPORTED_LANGUAGES)
    return g.lang

@app.url_value_preprocessor
def pull_lang_code(endpoint, values):
    if values is None:
        values = {}
    g.lang = values.pop('lang', None)

@app.before_request
def before_request():
    prefix = request.full_path.split('/')[1].rstrip('/ ?')
    if prefix not in SUPPORTED_LANGUAGES:
        # handle URLs that aren't properly prefixed with a langauge
        return redirect('/' + get_locale() + request.full_path, 302)

@app.route('/<lang>/')
def index():
    return 'Hello, world! ' + g.lang

@app.route('/<lang>/about/')
def about():
    return 'About ' + g.lang

if __name__ == '__main__':
    app.run(host='localhost', port=8080, debug=True)

【问题讨论】:

    标签: python google-app-engine flask


    【解决方案1】:

    我认为问题在于您的 before_request 回调。您在不检查请求中提供的 url 的情况下返回重定向,因此 flask 制定了重定向 url,然后尝试将用户重定向到它,然后当它发现 url 不存在时以 404 结束。

        if prefix not in SUPPORTED_LANGUAGES:
            # handle URLs that aren't properly prefixed with a langauge
            return redirect('/' + get_locale() + request.full_path, 302)
    

    我会首先编写代码来检查请求中提供的 url 以确保它是有效的。如果有效则重定向,否则中止(404)。

    希望这能让您了解如何解决问题。


    参考this thread

        if prefix not in SUPPORTED_LANGUAGES:
            url = request.full_path
            if is_path_valid(url):
                return redirect('/' + get_locale() + url, 302
            abort(404)
    

    is_path_valid()的定义:

    def is_path_valid(url):
        with app.test_client() as client:
            response = client.get(url)
            if response.status_code == 200:
                return True
            else:
                return False
    
    

    【讨论】:

    • “我会首先编写代码来检查请求中提供的 url 以确保它是有效的。”我怎么做?这基本上就是我的问题。我会更新得更清楚。
    • 使用此解决方案时,我在尝试访问不带前缀的 URL 时收到错误 RecursionError: maximum recursion depth exceeded(无论它们是否有效)。我不熟悉 test_client() 在 Flask 中的工作方式,但从堆栈跟踪中我猜测测试客户端的实例化涉及触发这些回调,从而导致无限递归循环。
    【解决方案2】:

    好的,我整理了如何在不使用测试客户端的情况下检查路径的有效性,使用this answer from another question about how to look up the route for a given path

    @app.before_request
    def before_request():
        path = request.full_path
        prefix = path.split('/')[1].rstrip('/ ?')
        if prefix not in SUPPORTED_LANGUAGES:
            prefixed_path = '/' + get_locale() + path
            route_from(prefixed_path)
            return redirect(prefixed_path, 302)
    
    def route_from(url, method = None):
        appctx = _app_ctx_stack.top
        reqctx = _request_ctx_stack.top
        if appctx is None:
            raise RuntimeError('Attempted to match a URL without the '
                               'application context being pushed. This has to be '
                               'executed when application context is available.')
    
        if reqctx is not None:
            url_adapter = reqctx.url_adapter
        else:
            url_adapter = appctx.url_adapter
            if url_adapter is None:
                raise RuntimeError('Application was not able to create a URL '
                                   'adapter for request independent URL matching. '
                                   'You might be able to fix this by setting '
                                   'the SERVER_NAME config variable.')
        parsed_url = url_parse(url)
        if parsed_url.netloc is not "" and parsed_url.netloc != url_adapter.server_name:
            raise NotFound()
        return url_adapter.match(parsed_url.path, method)
    

    我没有为route_from 分配返回值,因为我只是调用它以便raise NotFound 可以在无效路径上触发。它可能会被精简为一个更简单的功能,但我现在没有时间。

    现在before_request 对完整的有效路径不做任何事情,重定向缺少语言前缀但在其他方面有效的路径,以及对完全无效的路径不进行重定向的 404。

    【讨论】:

      猜你喜欢
      • 2021-11-08
      • 1970-01-01
      • 2015-01-30
      • 2018-04-30
      • 1970-01-01
      • 2018-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多