为所有Flask路线添加前缀

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

I have a prefix that I want to add to every route. Right now I add a constant to the route at every definition. Is there a way to do this automatically?

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"

回答 0

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

子安装在另一个WSGI容器中

假设您将在WSGI容器(mod_wsgi,uwsgi,gunicorn等)中运行此应用程序;您实际上需要将该应用程序作为该WSGI容器的子部分挂载在该前缀处(任何讲WSGI的东西都可以使用),并将APPLICATION_ROOTconfig值设置为您的前缀:

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)

代理请求到应用程序

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

  • 正如Miguel在回答中指出的那样,使用蓝图。
  • 或者使用DispatcherMiddlewarewerkzeug(或PrefixMiddlewareSU27的答案)到副安装在您使用的独立WSGI服务器应用程序。(有关使用代码,请参见上面的正确重新安装您的应用的示例)。

The answer depends on how you are serving this application.

Sub-mounted inside of another WSGI container

Assuming that you are going to run this application inside of a WSGI container (mod_wsgi, uwsgi, gunicorn, etc); you need to actually mount, at that prefix the application as a sub-part of that WSGI container (anything that speaks WSGI will do) and to set your APPLICATION_ROOT config value to your prefix:

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

Setting the APPLICATION_ROOT config value simply limit Flask’s session cookie to that URL prefix. Everything else will be automatically handled for you by Flask and Werkzeug’s excellent WSGI handling capabilities.

An example of properly sub-mounting your app

If you are not sure what the first paragraph means, take a look at this example application with Flask mounted inside of it:

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)

Proxying requests to the app

If, on the other hand, you will be running your Flask application at the root of its WSGI container and proxying requests to it (for example, if it’s being FastCGI’d to, or if nginx is proxy_pass-ing requests for a sub-endpoint to your stand-alone uwsgi / gevent server then you can either:

  • Use a Blueprint, as Miguel points out in his answer.
  • or use the DispatcherMiddleware from werkzeug (or the PrefixMiddleware from su27’s answer) to sub-mount your application in the stand-alone WSGI server you’re using. (See An example of properly sub-mounting your app above for the code to use).

回答 1

您可以将路线设置为蓝图:

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

You can put your routes in a blueprint:

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"

Then you register the blueprint with the application using a prefix:

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

回答 2

您应注意APPLICATION_ROOT并非用于此目的。

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

  1. 修改PATH_INFO以处理带前缀的网址。
  2. 修改SCRIPT_NAME以生成带前缀的网址。

像这样:

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不是这份工作,尽管它看起来象是。真是令人困惑。

You should note that the APPLICATION_ROOT is NOT for this purpose.

All you have to do is to write a middleware to make the following changes:

  1. modify PATH_INFO to handle the prefixed url.
  2. modify SCRIPT_NAME to generate the prefixed url.

Like this:

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()]

Wrap your app with the middleware, like this:

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)

Visit http://localhost:9010/foo/bar,

You will get the right result: The URL for this page is /foo/bar

And don’t forget to set the cookie domain if you need to.

This solution is given by Larivact’s gist. The APPLICATION_ROOT is not for this job, although it looks like to be. It’s really confusing.


回答 3

这比Flask / 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函数需要 a route作为第一个位置参数的事实。

您可以像这样使用它:

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

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

This is more of a python answer than a Flask/werkzeug answer; but it’s simple and works.

If, like me, you want your application settings (loaded from an .ini file) to also contain the prefix of your Flask application (thus, not to have the value set during deployment, but during runtime), you can opt for the following:

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

Arguably, this is somewhat hackish and relies on the fact that the Flask route function requires a route as a first positional argument.

You can use it like this:

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

NB: It is worth nothing that it is possible to use a variable in the prefix (for example by setting it to /<prefix>), and then process this prefix in the functions you decorate with your @app.route(...). If you do so, you obviously have to declare the prefix parameter in your decorated function(s). In addition, you might want to check the submitted prefix against some rules, and return a 404 if the check fails. In order to avoid a 404 custom re-implementation, please from werkzeug.exceptions import NotFound and then raise NotFound() if the check fails.


回答 4

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

但是,如果您希望在调试时运行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/

So, I believe that a valid answer to this is: the prefix should be configured in the actual server application that you use when development is completed. Apache, nginx, etc.

However, if you would like this to work during development while running the Flask app in debug, take a look at this gist.

Flask’s DispatcherMiddleware to the rescue!

I’ll copy the code here for posterity:

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

Now, when running the above code as a standalone Flask app, http://localhost:5000/spam/ will display Hello, world!.

In a comment on another answer, I expressed that I wished to do something like this:

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/

Applying DispatcherMiddleware to my contrived example:

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/

回答 5

另一种完全不同的方式与挂载点uwsgi

来自有关在同一过程中托管多个应用程序的文档(permalink)。

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

Another completely different way is with mountpoints in uwsgi.

From the doc about Hosting multiple apps in the same process (permalink).

In your uwsgi.ini you add

[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

If you don’t call your file main.py, you need to change both the mount and the module

Your main.py could look like this:

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

And a nginx config (again for completeness):

server {
  listen 80;
  server_name example.com

  location /foo {
    include uwsgi_params;
    uwsgi_pass unix:///temp/uwsgi.sock;
  }
}

Now calling example.com/foo/bar will display /foo/bar as returned by flask’s url_for('bar'), as it adapts automatically. That way your links will work without prefix problems.


回答 6

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

回答 7

我需要类似的所谓“上下文根”。我是使用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

I needed similar so called “context-root”. I did it in conf file under /etc/httpd/conf.d/ using WSGIScriptAlias :

myapp.conf:

<VirtualHost *:80>
    WSGIScriptAlias /myapp /home/<myid>/myapp/wsgi.py

    <Directory /home/<myid>/myapp>
        Order deny,allow
        Allow from all
    </Directory>

</VirtualHost>

So now I can access my app as : http://localhost:5000/myapp

See the guide – http://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html


回答 8

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

根目录中的KEEP 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,并让FLASK保留在根目录中

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)

My solution where flask and PHP apps coexist nginx and PHP5.6

KEEP Flask in root and PHP in subdirectories

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

Add 1 line

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

USE NESTED LOCATIONS for PHP and let FLASK remain in 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;
    #}
}

READ carefully https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

We need to understand location matching (none): If no modifiers are present, the location is interpreted as a prefix match. This means that the location given will be matched against the beginning of the request URI to determine a match. =: If an equal sign is used, this block will be considered a match if the request URI exactly matches the location given. ~: If a tilde modifier is present, this location will be interpreted as a case-sensitive regular expression match. ~*: If a tilde and asterisk modifier is used, the location block will be interpreted as a case-insensitive regular expression match. ^~: If a carat and tilde modifier is present, and if this block is selected as the best non-regular expression match, regular expression matching will not take place.

Order is important, from nginx’s “location” description:

To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.

It means:

First =. ("longest matching prefix" match)
Then implicit ones. ("longest matching prefix" match)
Then regex. (first match)

回答 9

对于仍在为此苦苦挣扎的人们,第一个示例确实有效,但是如果您拥有不受您控制的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,
    )

For people still struggling with this, the first example does work, but the full example is here if you have a Flask app that is not under your control:

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