Подтвердить что ты не робот

Добавить префикс для всех маршрутов 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"
4b9b3361

Ответ 1

Ответ зависит от того, как вы обслуживаете это приложение.

Sub-installed внутри другого контейнера 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 просто ограничивает куки файл флэшки для этого префикса URL-адреса. Все остальное будет автоматически обработано для вас средствами Flask и Werkzeug с превосходными возможностями обработки WSGI.

Пример правильной подстройки вашего приложения

Если вы не знаете, что означает первый абзац, посмотрите на это примерное приложение с установленной внутри него фляшкой:

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'd или nginx proxy_pass -ing запросы на субконечную точку на ваш автономный сервер uwsgi/gevent, то вы можете:

  • Используйте Blueprint, как указывает Мигель в его ответ.
  • или используйте DispatcherMiddleware из werkzeug (или PrefixMiddleware из su27 answer), чтобы подключить ваше приложение на автономном сервере WSGI вы используете. (См. Пример правильной подстройки вашего приложения выше для используемого кода).

Ответ 2

Вы можете поместить свои маршруты в план:

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

Ответ 3

Следует отметить, что 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 gist. APPLICATION_ROOT не для этой работы, хотя похоже, что это так. Это действительно запутанно.

Ответ 4

Это скорее ответ на 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

Возможно, это несколько хакерское и полагается на то, что для функции маршрута колбы требуется route в качестве первого позиционного аргумента.

Вы можете использовать его следующим образом:

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

NB: ничего не стоит использовать переменную в префиксе (например, установив ее на /<prefix>), а затем обработать этот префикс в функциях, которые вы украшаете с помощью @app.route(...). Если вы это сделаете, вам, очевидно, нужно объявить параметр prefix в ваших украшенных функциях. Кроме того, вы можете проверить представленный префикс на некоторые правила и вернуть 404, если проверка завершилась неудачно. Чтобы избежать 404 пользовательской повторной реализации, пожалуйста, from werkzeug.exceptions import NotFound, а затем raise NotFound(), если проверка завершилась с ошибкой.

Ответ 5

Итак, я считаю, что правильный ответ на этот вопрос: префикс должен быть настроен в фактическом серверном приложении, которое вы используете, когда разработка завершена. 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 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 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/

Ответ 6

Другим совершенно другим способом является mountpoints в 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'), так как он автоматически адаптируется. Таким образом, ваши ссылки будут работать без проблем с префиксами.

Ответ 7

Мне нужен был подобный так называемый "контекст-корень". Я сделал это в файле conf под /etc/httpd/conf.d/, используя WSGIScriptAlias:

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

Ответ 8

Я всегда предпочитаю использовать следующее, когда речь идет о добавлении префикса ко всему app:

app = Flask(__name__, root_path='/operators')

Чисто и понятно.

Ответ 9

Мое решение, где флеш и 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 ИСПОЛЬЗУЙТЕ NESTED LOCATIONS для 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 document root
    # concurs with nginx one
    #
    #location ~ /\.ht {
    #   deny all;
    #}
}

ПРОЧИТАЙТЕ внимательно https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms

Нам нужно понять соответствие местоположения (нет): если модификаторы отсутствуют, местоположение интерпретируется как совпадение префикса. Это означает, что указанное местоположение будет сопоставлено с началом URI запроса для определения соответствия. =: Если используется знак равенства, этот блок будет считаться совпадающим, если URI запроса точно соответствует указанному местоположению. ~: Если присутствует модификатор тильды, это местоположение будет интерпретироваться как регистрозависимое совпадение регулярного выражения. ~ *: Если используется модификатор тильды и звездочки, блок местоположения будет интерпретироваться как совпадение регулярного выражения без учета регистра. ^ ~: Если присутствует модификатор "Карат и тильда", и если этот блок выбран в качестве лучшего совпадения с нерегулярным выражением, сопоставление с регулярным выражением не будет иметь места.

Заказ важен, из nginx "местоположение" описание:

Чтобы найти местоположение, соответствующее заданному запросу, nginx сначала проверяет местоположения, определенные с помощью префиксных строк (префиксные местоположения). Среди них местоположение с самым длинным совпадающим префиксом выбирается и запоминается. Затем проверяются регулярные выражения в порядке их появления в файле конфигурации. Поиск регулярных выражений заканчивается при первом совпадении, и используется соответствующая конфигурация. Если совпадения с регулярным выражением не найдено, то используется конфигурация расположения префикса, запомненного ранее.

Это значит:

Первый = (соответствие "самый длинный совпадающий префикс"). Затем неявные. (совпадение "самый длинный совпадающий префикс") Затем регулярное выражение. (первый матч)

Ответ 10

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"