Статьи Утилиты Telegram YouTube VK Видео RuTube Отзывы

Версионирование REST API в Spring

26 февраля 2026

Тэги: Java, json, rest, Spring Boot, yaml.

Содержание

  1. Пример rest-контроллера
  2. Версионирование с помощью HTTP-заголовка
  3. Версионирование с помощью query-параметра
  4. Версионирование с помощью сегмента урла
  5. Выводы

По мере развития вашего приложения REST API может претерпевать значительные изменения. И при каждом изменении требуется сохранять обратную совместимость. Мы не можем просто так удалять эндпоинты, входные параметры этих эндпоинтов или часть возвращаемых полей, т.к. они могут использоваться на клиентской стороне. Безопасным можно считать только добавление новых полей или новых эндпоинтов, т.к. их ещё пока никто не использует.

Но как быть, если нам всё-таки требуется сделать несовместимые изменения в REST API? Тут нам на помощь придёт версионирование эндпоинтов.

Пример rest-контроллера

Предположим, у нас был эндпоинт, который возвращает строку приветствия для пользователя. Этот эндпоинт изначально не принимал никаких параметров.

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping(path = "/greetings", version = "1") // новый параметр version
    public ResponseDto oldGreeting() {
        return new ResponseDto("Привет!");
    }
}

Обратите внимание, что для аннотации @GetMapping (а также для @PostMapping, @PutMapping и т.д.) появился новый параметр version. Это произвольная строка, в которой мы можем сразу указать версию. Хоть этот параметр и является опциональным, лучше его заполнять для каждого эндпоинта (или на уровне всего контроллера), предусмотрев дальнейшую возможность изменения API.

Затем мы решили сделать более персонализированное приветствие, обращаясь к пользователю по имени. Для этого нам нужно, чтобы имя приходило как параметр запроса. Это изменение несовместимо со старым эндпоинтом, поэтому делаем вторую версию:

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping(path = "/greetings", version = "1")
    public ResponseDto oldGreeting() {
        return new ResponseDto("Привет!");
    }

    @GetMapping(path = "/greetings", version = "2")
    public ResponseDto newGreeting(@RequestParam String name) {
        return new ResponseDto(String.format("Привет, %s!", name));
    }
}

Интересно, что обе версии эндпоинта (т.е. маппинг на один и тот же урл) мы можем держать рядом в одном контроллере. До появления Spring Boot 4 это было невозможно.

Теперь осталось решить, как мы будем передавать номер версии в запросе. Есть несколько способов сделать это. Рассмотрим наиболее популярные:

  1. отдельный http-заголовок
  2. query-параметр
  3. часть сегмента урла

Версионирование с помощью HTTP-заголовка

Схема такая: клиент отправляет кастомный http-заголовок (например, X-API-Version), указывая в нём номер версии и в зависимости от этого заголовка на серверной стороне вызывается нужный эндпоинт.

Добавим в файл application.yaml параметр spring.mvc.apiversion.use.header и в качестве значение укажем имя этого заголовка:

spring:
  mvc:
    apiversion:
      use:
        header: X-API-Version

Теперь вызываем первую версию эндпоинта:

curl -H "X-API-Version: 1" -X GET "http://127.0.0.1:8080/api/greetings"

Ответ сервера:

{
  "message": "Привет!"
}

Вызываем вторую версию эндпоинта с указанием имени пользователя:

curl -H "X-API-Version: 2" -X GET "http://127.0.0.1:8080/api/greetings?name=devmark"

В ответе увидим имя из запроса:

{
  "message": "Привет, devmark!"
}

Теперь мы можем держать рядом разные версии этого эндпоинта.

Версионирование с помощью query-параметра

Альтернативой передаче номера версии в заголовке является передача номера версии как одного из параметров запроса.

Добавим в application.yaml параметр spring.mvc.apiversion.use.query-parameter и в качестве значения укажем желаемое название этого параметра:

spring:
  mvc:
    apiversion:
      use:
        query-parameter: version

Теперь мы можем вот так выполнять запрос к первому («старому») эндпоинту:

curl -X GET "http://127.0.0.1:8080/api/greetings?version=1"

А вот так к «новому»:

curl -X GET "http://127.0.0.1:8080/api/greetings?version=2&name=devmark"

Однако мне эта схема не нравится, т.к. происходит смешивание мета-данных запроса (version) и параметров бизнес-логики (name).

Версионирование с помощью сегмента урла

Третий способ - это указание номера версии в сегменте урла. То есть когда все эндпоинты первой версии начинаются с /api/v1, а второго - с /api/v2. Spring позволяет поддержать и такую схему.

Немного модифицируем @RequestMapping контроллера, добавив второй сегмент с произвольным именем, чтобы он стал общим для всех эндпоинтов:

@RestController
@RequestMapping("/api/v{version}")
public class ApiController {

    @GetMapping(path = "/greetings", version = "1")
    // ...

    @GetMapping(path = "/greetings", version = "2")
    // ...
}

Также добавим в файл application.yaml параметр spring.mvc.apiversion.use.path-segment. В качестве значения указываем порядковый номер сегмента, в котором указывается версия (начиная с 0). В данном случае у нас версия во втором сегменте, поэтому пишем 1.

spring:
  mvc:
    apiversion:
      use:
        path-segment: 1

Теперь делаем запрос к первому эндпоинту:

curl -X GET "http://127.0.0.1:8080/api/v1/greetings"

А вот так ко второму:

curl -X GET "http://127.0.0.1:8080/api/v2/greetings?name=devmark"

Выводы

Мы рассмотрели новый механизм версионирования REST API, который предлагает Spring Boot 4. Он позволяет держать разные версии эндпоинта рядом, что облегает поддержку обратной совместимости. В зависимости от той схемы версионирования, которая принята у вас на проекте, вы можете передавать номер версии в http-заголовке, в параметре запроса или в сегменте урла.


См. также


Комментарии

Добавить комментарий

×

devmark.ru