125

我有一个要添加到每条路线的前缀。现在我在每个定义处添加一个常量到路由。有没有办法自动做到这一点?

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

13 回答 13

122

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

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')
于 2013-09-23T21:24:12.867 回答
88

答案取决于您如何为该应用程序提供服务。

子安装在另一个 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请求子端点到您的独立uwsgi/gevent服务器,那么您可以:

  • 正如 Miguel 在他的回答中指出的那样,使用蓝图。
  • 使用DispatcherMiddlewarefrom werkzeug(或PrefixMiddlewarefrom su27's answer)将您的应用程序安装在您正在使用的独立 WSGI 服务器中。(有关要使用的代码,请参阅上面正确安装您的应用程序的示例)。
于 2013-09-23T19:57:47.667 回答
62

您应该注意,这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 的 gist给出。不适合这份APPLICATION_ROOT工作,虽然看起来是这样。这真的很混乱。

于 2016-03-16T10:57:05.113 回答
13

这更像是一个 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

可以说,这有点骇人听闻,并且依赖于 Flask 路由函数需要aroute作为第一个位置参数的事实。

你可以像这样使用它:

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

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

于 2016-06-17T09:51:26.827 回答
5

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

但是,如果您希望在调试中运行 Flask 应用程序的同时在开发期间工作,请查看这个 gist

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)

现在,当将上述代码作为独立的 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/
于 2016-05-18T19:52:07.923 回答
3
from flask import Flask

app = Flask(__name__)

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

if __name__ == "__main__":
    app.run(debug='True', port=4444)


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

@bp.route('/')
def test():
    return "success"
于 2019-01-10T11:30:27.713 回答
2

另一种完全不同的方式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,则需要同时更改 themount和 themodule

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为由 flask 的返回url_for('bar'),因为它会自动适应。这样,您的链接将不会出现前缀问题。

于 2017-11-07T10:13:53.187 回答
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

于 2017-08-11T18:14:45.073 回答
1

我的flask和PHP应用共存nginx和PHP5.6的解决方案

在根目录中保留 Flask,在子目录中保留 PHP

sudo vi /etc/php/5.6/fpm/php.ini

添加 1 行

cgi.fix_pathinfo=0
sudo vi /etc/php/5.6/fpm/pool.d/www.conf
listen = /run/php/php5.6-fpm.sock

uwsgi

sudo vi /etc/nginx/sites-available/default

为 PHP 使用 NESTED LOCATIONS 并让 FLASK 保留在 root 中

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    # SSL configuration
    #
    # listen 443 ssl default_server;
    # listen [::]:443 ssl default_server;
    #
    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

    root /var/www/html;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.php index.nginx-debian.html;

    server_name _;

    # Serve a static file (ex. favico) outside static dir.
    location = /favico.ico  {    
        root /var/www/html/favico.ico;    
    }

    # Proxying connections to application servers
    location / {
        include            uwsgi_params;
        uwsgi_pass         127.0.0.1:5000;
    }

    location /pcdp {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    location /phpmyadmin {
        location ~* \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php/php5.6-fpm.sock;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #   include snippets/fastcgi-php.conf;
    #
    #   # With php7.0-cgi alone:
    #   fastcgi_pass 127.0.0.1:9000;
    #   # With php7.0-fpm:
    #   fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #   deny all;
    #}
}

仔细阅读 https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

我们需要了解位置匹配(无):如果不存在修饰符,则位置被解释为前缀匹配。这意味着给定的位置将与请求 URI 的开头进行匹配以确定匹配。=:如果使用等号,如果请求 URI 与给定位置完全匹配,则此块将被视为匹配。~:如果存在波浪号修饰符,则此位置将被解释为区分大小写的正则表达式匹配。~*:如果使用波浪号和星号修饰符,则位置块将被解释为不区分大小写的正则表达式匹配。^~:如果存在克拉和波浪号修饰符,并且如果该块被选为最佳非正则表达式匹配,则不会进行正则表达式匹配。

顺序很重要,来自 nginx 的“位置”描述:

为了找到与给定请求匹配的位置,nginx 首先检查使用前缀字符串(前缀位置)定义的位置。其中,匹配前缀最长的位置被选中并记忆。然后按照它们在配置文件中出现的顺序检查正则表达式。正则表达式的搜索在第一次匹配时终止,并使用相应的配置。如果找不到与正则表达式的匹配项,则使用前面记住的前缀位置的配置。

它的意思是:

First =. ("longest matching prefix" match)
Then implicit ones. ("longest matching prefix" match)
Then regex. (first match)
于 2018-11-08T06:31:41.037 回答
1

对于仍在为此苦苦挣扎的人,第一个示例确实有效,但如果您有一个不受您控制的 Flask 应用程序,则完整示例在这里:

from os import getenv
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.serving import run_simple
from custom_app import app

application = DispatcherMiddleware(
    app, {getenv("REBROW_BASEURL", "/rebrow"): app}
)

if __name__ == "__main__":
    run_simple(
        "0.0.0.0",
        int(getenv("REBROW_PORT", "5001")),
        application,
        use_debugger=False,
        threaded=True,
    )
于 2020-03-19T05:56:40.837 回答
1

烧瓶蓝图中,我们可以使用 -

app = Flask(__name__)

app.config['APPLICATION_ROOT'] = '/prefix-text'

任何想要在flask-restful 中做事的人都可以使用 -

文档链接

app = Flask(__name__)

api = Api(app, prefix='/pefix-text')

现在,您所有的路线都将以/prefix-text. 只需确保url_for('link')在您可能只使用过/link.

于 2021-07-01T18:06:32.437 回答
0

我认为su27的回答是对的。我正在使用 gevent,这是我的代码,它工作正常:

from gevent import pywsgi

# your flask code ...
# app = Flask(__name__)

if __name__ == "__main__":
    class MyHandler(pywsgi.WSGIHandler):
        def get_environ(self):
            prefix = "/your_prefix"
            env = super().get_environ()
            if env['PATH_INFO'].startswith(prefix):
                env['PATH_INFO'] = env['PATH_INFO'][len(prefix):]
                env['SCRIPT_NAME'] = prefix
            return env
    
    server = pywsgi.WSGIServer(('', 8080), app, handler_class=MyHandler)
    server.serve_forever()
于 2021-04-07T08:38:12.773 回答
0

从我上面看到的所有答案来看,它们要么过于简单,要么过于复杂。

也就是说,我喜欢使用嵌套蓝图来完成它:

from .blueprints import blueprint1, blueprint2, blueprint3, etc


app = Flask(__name__)

url_prefix = "/abc/123"
parent = Blueprint('index', __name__, url_prefix=url_prefix)

index.register_blueprint(blueprint1)
index.register_blueprint(blueprint2)
index.register_blueprint(blueprint3)
app.register_blueprint(index)

这样,您基本上将您的子蓝图链接到父蓝图,您可以在其中定义前缀。这在此处记录。

在您的示例中,您只需将其重写为:

blueprint1 = Blueprint('blueprint1', __name__)

@blueprint1.route("/")
def index_page():
  return "Index page"

@blueprint1.route("/about")
def about_page():
  return "About page"
于 2022-02-18T14:47:25.803 回答