为所有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" 

答案取决于你如何为这个应用程序提供服务。

分装在另一个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/" 

设置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) 

代理请求到应用程序

另一方面,如果您将在其WSGI容器的根目录上运行Flask应用程序,并对其进行代理请求(例如,如果它正在被FastCGI指定,或者如果nginx是proxy_pass端点到您的独立gevent / gevent服务器,然后您可以:

  • 正如Miguel在答案中指出的那样,使用蓝图。
  • 或者使用来自werkzeugDispatcherMiddleware (或PrefixMiddleware 的答案中的PrefixMiddleware )将您的应用程序下载到您正在使用的独立WSGI服务器中。 (请参阅上面的代码,以便正确地在您的应用程序中使用代码)。

你可以把你的路线放在一个蓝图中:

 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" 

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

 app = Flask(__name__) app.register_blueprint(bp, url_prefix='/abc/123') 

你应该注意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()] 

用中间件包装你的应用程序,就像这样:

 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) 

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

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

如果需要的话,不要忘了设置cookie域。

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

这比python / werkzeug的答案更像python的答案; 但它很简单,有效。

如果像我一样,您希望您的应用程序设置(从.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 

可以说,这是有点冒险的,并且依赖于Flask路由函数需要 route作为第一位置参数的事实。

你可以像这样使用它:

 app = Flask(__name__) app.route = prefix_route(app.route, '/your_prefix') 

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

所以,我相信一个有效的答案是:前缀应该在开发完成时使用的实际服务器应用程序中进行配置。 Apache,nginx等

但是,如果您希望在开发过程中使用Flask应用程序进行调试,请参阅此主题 。

Flask的DispatcherMiddleware来拯救!

我将在这里复制代码为后人:

 "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) 

现在,将上面的代码作为独立的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/ 

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/ 

我需要类似的所谓的“上下文根”。 我使用WSGIScriptAlias在/etc/httpd/conf.d/下的conf文件中完成了这个工作:

myapp.conf:

 <VirtualHost *:80> WSGIScriptAlias /myapp /home/<myid>/myapp/wsgi.py <Directory /home/<myid>/myapp> Order deny,allow Allow from all </Directory> </VirtualHost> 

所以现在我可以访问我的应用程序: http:// localhost:5000 / myapp

请参阅指南 – http://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html

另一种完全不同的方式是在uwsgi使用挂载点

从文档主持多个应用程序在同一过程 ( 永久链接 )。

在你的uwsgi.ini添加

 [uwsgi] mount = /foo=main.py manage-script-name = true # also stuff which is not relevant for this, but included for completeness sake: module = main callable = app socket = /tmp/uwsgi.sock 

如果你不叫你的文件main.py ,你需要改变mountmodule

你的main.py可能是这样的:

 from flask import Flask, url_for app = Flask(__name__) @app.route('/bar') def bar(): return "The URL for this page is {}".format(url_for('bar')) # end def 

和一个nginx配置(为了完整性):

 server { listen 80; server_name example.com location /foo { include uwsgi_params; uwsgi_pass unix:///temp/uwsgi.sock; } } 

现在调用example.com/foo/bar会显示/foo/bar作为瓶子的url_for('bar') ,因为它自动适应。 这样你的链接将工作,没有前缀的问题。