2025, Oct 16 00:16

Как добавить префикс /api ко всем эндпоинтам Flask через url_prefix

Почему APPLICATION_ROOT и SCRIPT_NAME не добавляют префикс /api к маршрутам Flask и как применять url_prefix в блюпринтах или при регистрации, чтобы избежать 404.

Как добавить префикс /api ко всем эндпоинтам Flask, не сломав маршруты

Помещать все конечные точки под общий путь вроде /api — частая задача. Интуитивно хочется задать ключи конфигурации, такие как APPLICATION_ROOT или SCRIPT_NAME, и ожидать, что Flask автоматически добавит префикс. На практике этот подход приводит к 404 при обращении к /api/... — если только вы не пропишете префикс в каждом маршруте или явно не зададите его у блюпринта.

Настройка, которая приводит к 404

Ниже — минимальная структура, которая на первый взгляд выглядит корректно, но для /api/... возвращает 404, потому что маршруты остаются на корне /, а не переходят под /api.

# core.py
from flask import Flask

site = Flask(__name__)

# Пытаемся смонтировать всё под /api
site.config["SCRIPT_NAME"] = "/api/"
# Альтернативный вариант:
# site.config["APPLICATION_ROOT"] = "/api/"
# modules/accounts.py
from flask import Blueprint

acct_bp = Blueprint("accounts_bp", __name__)

@acct_bp.route("/", methods=["GET"])
def fetch_root():
    return "Called get method"

@acct_bp.route("/data", methods=["GET"])
def fetch_data():
    return "Called data method"
# main.py
from core import site
from modules.accounts import acct_bp

site.register_blueprint(acct_bp)

if __name__ == "__main__":
    site.run(debug=True)

Теперь вызов /api/ возвращает 404. Маршруты существуют, но привязаны к / и /data на корне приложения, а не под /api.

Почему так происходит

APPLICATION_ROOT и SCRIPT_NAME определяют базовый путь, под которым приложение смонтировано на стороне WSGI-сервера. Они не добавляют /api к вашим маршрутам автоматически во время разработки, поэтому @...route("/") остаётся по адресу /, а не /api/. Это несоответствие и приводит к 404 при запросах к /api/...

Решение: используйте url_prefix в самом блюпринте или при регистрации

Правильный способ сгруппировать набор маршрутов под общим путём — использовать url_prefix. Его можно задать либо при создании блюпринта, либо при его регистрации. В обоих случаях результат одинаковый.

Вариант A: задайте префикс прямо в блюпринте.

# core.py
from flask import Flask

site = Flask(__name__)
# modules/accounts.py
from flask import Blueprint

acct_bp = Blueprint("accounts_bp", __name__, url_prefix="/api")

@acct_bp.route("/", methods=["GET"])
def fetch_root():
    return "Called get method"

@acct_bp.route("/data", methods=["GET"])
def fetch_data():
    return "Called data method"
# main.py
from core import site
from modules.accounts import acct_bp

site.register_blueprint(acct_bp)

if __name__ == "__main__":
    site.run(debug=True)
python main.py

>> curl http://127.0.0.1:5000/api/
Called get method
>> curl http://127.0.0.1:5000/api/data
Called data method

Вариант B: оставить блюпринт «чистым» и применить префикс при регистрации.

# core.py
from flask import Flask

site = Flask(__name__)
# modules/accounts.py
from flask import Blueprint

acct_bp = Blueprint("accounts_bp", __name__)

@acct_bp.route("/", methods=["GET"])
def fetch_root():
    return "Called get method"

@acct_bp.route("/data", methods=["GET"])
def fetch_data():
    return "Called data method"
# main.py
from core import site
from modules.accounts import acct_bp

site.register_blueprint(acct_bp, url_prefix="/api")

if __name__ == "__main__":
    site.run(debug=True)
python main.py

>> curl http://127.0.0.1:5000/api/
Called get method
>> curl http://127.0.0.1:5000/api/data
Called data method

Почему это важно

Попытка полагаться на APPLICATION_ROOT или SCRIPT_NAME для изменения карты URL в разработке приводит к запутанным 404 и скрывает истинную причину проблемы. Понимание того, что эти ключи описывают место монтирования приложения и не переписывают маршруты, упрощает картину. Использование url_prefix делает маршрутизацию явной, предсказуемой и согласованной между модулями.

Выводы

Если вам нужно, чтобы все конечные точки жили под /api, используйте url_prefix. Задавайте его либо в самом блюпринте, либо при регистрации, и не рассчитывайте на APPLICATION_ROOT или SCRIPT_NAME для добавления префикса на этапе разработки. Так дерево маршрутов остаётся прозрачным, а неожиданных 404 при обращении к /api/ удастся избежать.

Статья основана на вопросе на StackOverflow от Mr.Singh и ответе Ajeet Verma.