为所有Flask路由添加前缀

Eva*_*ahn 84 python routes flask

我有一个前缀,我想添加到每个路由.现在我在每个定义中为路径添加一个常量.有没有办法自动执行此操作?

PREFIX = "/abc/123"

@app.route(PREFIX + "/")
def index_page():
  return "This is a website about burritos"

@app.route(PREFIX + "/about")
def about_page():
  return "This is a website about burritos"
Run Code Online (Sandbox Code Playgroud)

Mig*_*uel 84

您可以将路线放在蓝图中:

bp = Blueprint('burritos', __name__,
                        template_folder='templates')

@bp.route("/")
def index_page():
  return "This is a website about burritos"

@bp.route("/about")
def about_page():
  return "This is a website about burritos"
Run Code Online (Sandbox Code Playgroud)

然后使用前缀向应用程序注册蓝图:

app = Flask(__name__)
app.register_blueprint(bp, url_prefix='/abc/123')
Run Code Online (Sandbox Code Playgroud)

  • 不同之处在于,在`register_blueprint`调用中使用URL前缀使应用程序可以自由地将蓝图"挂载"到任何地方,甚至可以在不同的URL上多次安装相同的蓝图.如果将前缀放在蓝图中,则可以使应用程序更容易,但灵活性较低. (4认同)
  • 请注意,有必要在*blueprint.route修饰函数之后注册蓝图*. (3认同)
  • 嗨米格尔; 你是否知道为蓝图注册url_prefix和使用`app.register_blueprint`之间的区别,以及在实例化上面的Blueprint对象时通过传递`url_prefix ='/ abc/123`注册它之间的区别?谢谢! (2认同)

Sea*_*ira 66

答案取决于您如何提供此应用程序.

子安装在另一个WSGI容器内

假设您要在WSGI容器(mod_wsgi,uwsgi,gunicorn等)中运行此应用程序; 你需要在该前缀下实际挂载应用程序作为WSGI容器的子部分(任何说WSGI的东西)并将APPLICATION_ROOT配置值设置为你的前缀:

app.config["APPLICATION_ROOT"] = "/abc/123"

@app.route("/")
def index():
    return "The URL for this page is {}".format(url_for("index"))

# Will return "The URL for this page is /abc/123/"
Run Code Online (Sandbox Code Playgroud)

设置APPLICATION_ROOT配置值只是将Flask的会话cookie限制为该URL前缀.Flask和Werkzeug优秀的WSGI处理功能将为您自动处理其他所有内容.

正确子安装您的应用程序的示例

如果您不确定第一段的含义,请查看此示例应用程序,其中安装了Flask:

from flask import Flask, url_for
from werkzeug.serving import run_simple
from werkzeug.wsgi import DispatcherMiddleware

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/abc/123'

@app.route('/')
def index():
    return 'The URL for this page is {}'.format(url_for('index'))

def simple(env, resp):
    resp(b'200 OK', [(b'Content-Type', b'text/plain')])
    return [b'Hello WSGI World']

app.wsgi_app = DispatcherMiddleware(simple, {'/abc/123': app.wsgi_app})

if __name__ == '__main__':
    app.run('localhost', 5000)
Run Code Online (Sandbox Code Playgroud)

向应用程序提供请求

另一方面,如果您将在其WSGI容器的根目录下运行Flask应用程序并代理对它的请求(例如,如果它是FastCGI,或者如果nginx是proxy_pass-ing请求子端点)对于您的独立uwsgi/ gevent服务器,您可以:

  • 正如米格尔在答案中指出的那样,使用蓝图.
  • 或者使用DispatcherMiddlewarefrom werkzeug(或PrefixMiddleware来自su27的答案)将您的应用程序分装到您正在使用的独立WSGI服务器中.(请参阅上面正确的子安装应用程序以获取要使用的代码的示例).

  • @jknupp - 这就是问题 - 您需要将应用程序实际挂载为更大的应用程序的子部分(任何说WSGI的人都会这样做).我已经掀起了[示例要点](https://gist.github.com/svieira/3434cbcaf627e50a4808)并更新了我的答案,以便更清楚地说明我假设是一个子装载的WSGI环境,而不是一个独立的环境代理后面的WSGI环境,仅转发子路径请求. (4认同)
  • 这可以使用`DispatcherMiddleware`方法,在单独运行flask时使用.在Gunicorn后面奔跑时似乎无法让这个工作. (3认同)

su2*_*u27 38

你应该注意,这APPLICATION_ROOT不是为了这个目的.

您所要做的就是编写一个中间件来进行以下更改:

  1. 修改PATH_INFO以处理前缀url.
  2. 修改SCRIPT_NAME以生成带前缀的URL.

像这样:

class PrefixMiddleware(object):

    def __init__(self, app, prefix=''):
        self.app = app
        self.prefix = prefix

    def __call__(self, environ, start_response):

        if environ['PATH_INFO'].startswith(self.prefix):
            environ['PATH_INFO'] = environ['PATH_INFO'][len(self.prefix):]
            environ['SCRIPT_NAME'] = self.prefix
            return self.app(environ, start_response)
        else:
            start_response('404', [('Content-Type', 'text/plain')])
            return ["This url does not belong to the app.".encode()]
Run Code Online (Sandbox Code Playgroud)

用中间件包装你的应用程序,如下所示:

from flask import Flask, url_for

app = Flask(__name__)
app.debug = True
app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix='/foo')


@app.route('/bar')
def bar():
    return "The URL for this page is {}".format(url_for('bar'))


if __name__ == '__main__':
    app.run('0.0.0.0', 9010)
Run Code Online (Sandbox Code Playgroud)

访问http://localhost:9010/foo/bar,

你会得到正确的结果: The URL for this page is /foo/bar

如果需要,请不要忘记设置cookie域.

这个解决方案由Larivact的要点给出.这APPLICATION_ROOT不是为了这份工作,虽然它看起来像是.这真的令人困惑.

  • 感谢您添加此答案。尝试了此处发布的其他解决方案,但这是唯一对我有用的解决方案。我使用wfastcgi.py在IIS上部署了A +++ (2认同)
  • 如果您正在使用gunicorn,则已经支持SCRIPT_NAME。将其设置为环境变量或将其作为http标头传递:http://docs.gunicorn.org/en/stable/faq.html (2认同)

7he*_*.tk 8

这更像是一个python答案,而不是Flask/werkzeug答案; 但它很简单而且很有效.

如果像我一样,您希望应用程序设置(从.ini文件加载)也包含Flask应用程序的前缀(因此,不要在部署期间设置值,但在运行时),您可以选择以下内容:

def prefix_route(route_function, prefix='', mask='{0}{1}'):
  '''
    Defines a new route function with a prefix.
    The mask argument is a `format string` formatted with, in that order:
      prefix, route
  '''
  def newroute(route, *args, **kwargs):
    '''New function to prefix the route'''
    return route_function(mask.format(prefix, route), *args, **kwargs)
  return newroute
Run Code Online (Sandbox Code Playgroud)

可以说,这是有点hackish的,并且依赖于该瓶路由功能的事实,需要一个route作为第一个位置参数.

你可以像这样使用它:

app = Flask(__name__)
app.route = prefix_route(app.route, '/your_prefix')
Run Code Online (Sandbox Code Playgroud)

注意:在前缀中使用变量(例如通过将其设置为/<prefix>)是可能的,然后在您使用您的装饰的函数中处理此前缀@app.route(...).如果这样做,您显然必须prefix在装饰函数中声明参数.此外,您可能希望根据某些规则检查提交的前缀,如果检查失败则返回404.为了避免404定制的重新实施,请from werkzeug.exceptions import NotFound然后raise NotFound()如果检查失败.


Mon*_*pit 5

因此,我认为对此的有效答案是:应该在开发完成时使用的实际服务器应用程序中配置前缀。Apache、nginx 等

但是,如果您希望在调试中运行 Flask 应用程序的同时在开发过程中使用它,请查看此要点

FlaskDispatcherMiddleware来拯救你了!

我将在这里复制代码以供后代使用:

"Serve a Flask app on a sub-url during localhost development."

from flask import Flask


APPLICATION_ROOT = '/spam'


app = Flask(__name__)
app.config.from_object(__name__)  # I think this adds APPLICATION_ROOT
                                  # to the config - I'm not exactly sure how!
# alternatively:
# app.config['APPLICATION_ROOT'] = APPLICATION_ROOT


@app.route('/')
def index():
    return 'Hello, world!'


if __name__ == '__main__':
    # Relevant documents:
    # http://werkzeug.pocoo.org/docs/middlewares/
    # http://flask.pocoo.org/docs/patterns/appdispatch/
    from werkzeug.serving import run_simple
    from werkzeug.wsgi import DispatcherMiddleware
    app.config['DEBUG'] = True
    # Load a dummy app at the root URL to give 404 errors.
    # Serve app at APPLICATION_ROOT for localhost development.
    application = DispatcherMiddleware(Flask('dummy_app'), {
        app.config['APPLICATION_ROOT']: app,
    })
    run_simple('localhost', 5000, application, use_reloader=True)
Run Code Online (Sandbox Code Playgroud)

现在,当将上述代码作为独立的 Flask 应用程序运行时,http://localhost:5000/spam/将显示Hello, world!.

在对另一个答案的评论中,我表示我希望做这样的事情:

from flask import Flask, Blueprint

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
app.run()

# I now would like to be able to get to my route via this url:
# http://host:8080/api/some_submodule/record/1/
Run Code Online (Sandbox Code Playgroud)

适用DispatcherMiddleware于我人为的例子:

from flask import Flask, Blueprint
from flask.serving import run_simple
from flask.wsgi import DispatcherMiddleware

# Let's pretend module_blueprint defines a route, '/record/<id>/'
from some_submodule.flask import module_blueprint

app = Flask(__name__)
app.config['APPLICATION_ROOT'] = '/api'
app.register_blueprint(module_blueprint, url_prefix='/some_submodule')
application = DispatcherMiddleware(Flask('dummy_app'), {
    app.config['APPLICATION_ROOT']: app
})
run_simple('localhost', 5000, application, use_reloader=True)

# Now, this url works!
# http://host:8080/api/some_submodule/record/1/
Run Code Online (Sandbox Code Playgroud)