Зависимость для версионирования
Фабрика versioning() — способ пометить эндпоинт как версионированный. Она возвращает экземпляр VersioningSupport, пригодный для использования в Depends(). Эндпоинт без такой зависимости в версионировании не участвует и остаётся только в той версии, где объявлен.
Семантика until
versioning(*, until: int | None = None) управляет диапазоном версий, в которых доступен эндпоинт:
| Значение | Поведение |
|---|---|
until=None (по умолчанию) |
Эндпоинт доступен со своей версии и во всех последующих |
until=N |
Эндпоинт доступен со своей версии по версию N включительно |
Дополнительные правила:
- Если у роута несколько
versioning-зависимостей (например, одна на роутере и одна на эндпоинте), действует минимальный из указанныхuntil. untilменьше версии, в которой объявлен эндпоинт, — противоречие: библиотека выдастUserWarning, эндпоинт останется доступен в своей версии, но не будет унаследован никуда.
Способы подключения
Зависимость принимается везде, где FastAPI принимает зависимости:
from fastapi import APIRouter, Depends, FastAPI
from fastapi_easy_versioning import versioning
v1_app = FastAPI(api_version=1)
# На эндпоинте — через декоратор
@v1_app.get('/endpoint', dependencies=[Depends(versioning())])
def endpoint() -> None: ...
# Через add_api_route
v1_app.add_api_route('/added', endpoint, dependencies=[Depends(versioning(until=2))])
# Сразу на весь роутер — версионируются все его эндпоинты
router = APIRouter(dependencies=[Depends(versioning())])
@router.get('/router-endpoint')
def router_endpoint() -> None: ...
v1_app.include_router(router)
Чтение метаданных в эндпоинте
Зависимость можно внедрить в эндпоинт — тогда она возвращает именованный кортеж VersionInfo с разрешённой конфигурацией версионирования роута:
origin— версия, в которой эндпоинт объявлен;until— последняя версия, в которой эндпоинт доступен. Еслиuntilне задавался явно, это последняя существующая версия API.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi_easy_versioning import VersionInfo, versioning
v1_app = FastAPI(api_version=1)
@v1_app.get('/endpoint')
def endpoint(version: Annotated[VersionInfo, Depends(versioning())]) -> str:
return f"Доступен с версии {version.origin} до версии {version.until}"
Инъекция работает и в WebSocket-эндпоинтах:
from typing import Annotated
from fastapi import Depends, FastAPI, WebSocket
from fastapi_easy_versioning import VersionInfo, versioning
v1_app = FastAPI(api_version=1)
@v1_app.websocket('/ws')
async def ws_endpoint(
websocket: WebSocket,
version: Annotated[VersionInfo, Depends(versioning())],
) -> None:
await websocket.accept()
await websocket.send_text(f"Доступен с версии {version.origin}")
await websocket.close()
Диагностика
Если версионирование не инициализировано, попытка внедрить VersionInfo завершится RuntimeError с перечислением возможных причин:
VersioningMiddlewareне добавлен в приложение, монтирующее версии;- у субприложения не задан
FastAPI(api_version=<int>); - роут зарегистрирован после первого запроса, а
rebuild_versioningне вызывался.