Короткий ответ
Версионирование через URL (/v1/...) — самое явное и простое для отладки, но "захватывает" версию во всех ссылках; через заголовок (Api-Version: 2) или content negotiation (Accept: application/vnd.myapi.v2+json) — чище с точки зрения REST (URL остаётся постоянным идентификатором ресурса), но менее заметно и сложнее для отладки curl'ом и кэширования прокси.
Как это работает подробнее
при версионировании через URL маршрутизация выбирает обработчик сразу по префиксу пути; при версионировании через заголовок один и тот же путь обслуживается разной логикой в зависимости от значения заголовка, что требует дополнительной диспетчеризации внутри приложения или на уровне API-гейтвея.
Пример кода
example.txt
# Версионирование через URL
GET /v2/orders/42 HTTP/1.1
Host: api.example.com
# Версионирование через заголовок
GET /orders/42 HTTP/1.1
Host: api.example.com
Api-Version: 2
# Content negotiation
GET /orders/42 HTTP/1.1
Host: api.example.com
Accept: application/vnd.example.v2+jsonЧто использовать на практике
- Рекомендация. выбор зависит от аудитории API: для публичного API с внешними интеграторами версия в URL снижает порог входа (видно в документации, легко протестировать вручную); для внутреннего API, тесно интегрированного с API-гейтвеем и кэшированием по URL, версия в заголовке может быть предпочтительнее, чтобы не размножать записи кэша на каждую версию одного и того же логического ресурса.
Подводные камни
- Риски. смешивать подходы в рамках одного API без единой политики (часть эндпоинтов версионируется в URL, часть — в заголовке), что усложняет документацию и клиентские SDK; не определять явного поведения при отсутствии заголовка версии (нужно решить — использовать последнюю версию по умолчанию или отвечать ошибкой, и задокументировать это решение).