Add a prefix to all Flask routes

后端 未结 10 833
你的背包
你的背包 2020-11-22 09:40

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?

PR         


        
相关标签:
10条回答
  • 2020-11-22 10:33

    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.

    0 讨论(0)
  • 2020-11-22 10:35

    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

    0 讨论(0)
  • 2020-11-22 10:41

    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)
    
    0 讨论(0)
  • 2020-11-22 10:42

    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/
    
    0 讨论(0)
提交回复
热议问题