为所有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在答案中指出的那样,使用蓝图。
- 或者使用来自
werkzeug
的DispatcherMiddleware
(或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
不是为了这个目的。
你所要做的就是编写一个中间件来进行下列修改:
- 修改
PATH_INFO
来处理前缀的url。 - 修改
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
,你需要改变mount
和module
你的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')
,因为它自动适应。 这样你的链接将工作,没有前缀的问题。