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.