Создание документации вручную — процесс трудоёмкий и часто рутинный. В этой статье рассмотрим основные возможности Swagger для документирования REST API в приложении на Spring Boot.
Используемые версии
Java 21
Spring Boot 3.3.5 | 2.7.18
SpringDoc 2.6.0 | 1.8.0
Swagger 2.2.25
Изменения статьи
29.06.22: Обновил до Java 17. Обновил все зависимости до актуальных.
11.02.23: Обновил SpringBoot до 3.0.2. Обновил остальные зависимости. Добавил раздел с авторизацией в Swagger UI.
04.11.24: Обновил Java до 21. Обновил SpringBoot до 3.3.5. Обновил остальные зависимости.
Что такое Swagger?
Swagger — это инструмент, который позволяет разработчикам описывать структуру своих API, а также генерировать интерактивную документацию, клиентские библиотеки и серверные модули для их реализации на различных языках.
Основой Swagger является спецификация OpenAPI (OAS), представляющая единый и чёткий способ описания REST API, включая эндпойнты, параметры, модели запросов и ответов и другие аспекты API.
В данной статье рассматривается пример генерации OAS на основе существующих REST-контроллеров. Мы разметим контроллеры и их эндпойнты аннотациями, чтобы автоматически создать OAS.
В данной статье рассматривается пример генерации OAS на основе уже существующих REST контроллеров. Мы разметим контроллеры и эндпойнты аннотациями, на основе которых будет сгенерирована OAS.
Почему SpringDoc, а не SpringFox?
Существует множество статей, описывающих генерацию Swagger UI с помощью библиотеки SpringFox. Однако я решил использовать SpringDoc по нескольким причинам.
SpringFox больше не поддерживается: последний коммит был сделан в 2020 году, и количество нерешённых задач (issue) уже превысило 200. В какой-то момент я столкнулся с багом в SpringFox, который не будет исправлен, и решил искать альтернативу.
SpringDoc оказался оптимальным выбором — он активно развивается и поддерживает новые версии Spring Boot, включая Spring Boot 3. Переход с SpringFox на SpringDoc прошёл быстро и успешно.
Swagger также может сгенерировать код клиента или сервера на основе OAS с помощью Swagger Codegen. Я лично не использую этот генератор, так как созданный автоматически код часто требует значительной доработки и проще написать его вручную.
Демо проект с REST API
Для начала документирования API необходимо сначала создать его. Вы можете пропустить эту главу.
Добавим несколько простых контроллеров и одно DTO. Наша система — это программа лояльности для пользователей. В рамках данного примера мы ограничимся только слоем контроллеров, пропустив серверную и репозиторную части. Это позволит сосредоточиться на документации, а бизнес-логику временно разместим непосредственно в контроллерах.
В качестве DTO будет использоваться класс UserDto, представляющий пользователя системы. У него пять полей, из которых три обязательны.
UserDto.java
public class UserDto {
private String key;
private String name;
private Long points = 0L;
private Gender gender;
private LocalDateTime regDate = LocalDateTime.now();
public UserDto() {
}
public UserDto(String key, String name, Gender gender) {
this.key = key;
this.name = name;
this.gender = gender;
}
public static UserDto of(String key, String value, Gender gender) {
return new UserDto(key, value, gender);
}
// getters and setters
}public enum Gender {
MAN, WOMAN
}
Для взаимодействия с бизнес-логикой добавим три контроллера: UserController, PointController и SecretController.
UserController: Обеспечивает добавление, обновление и получение данных пользователей.
UserController.java
@RestController
@RequestMapping("/api/user")
public class UserController {
private final Map<String, UserDto> repository;
public UserController(Map<String, UserDto> repository) {
this.repository = repository;
}
@PutMapping(produces = APPLICATION_JSON_VALUE)
public HttpStatus registerUser(@RequestBody UserDto userDto) {
repository.put(userDto.getKey(), userDto);
return HttpStatus.OK;
}
@PostMapping(produces = APPLICATION_JSON_VALUE)
public HttpStatus updateUser(@RequestBody UserDto userDto) {
if (!repository.containsKey(userDto.getKey())) return HttpStatus.NOT_FOUND;
repository.put(userDto.getKey(), userDto);
return HttpStatus.OK;
}
@GetMapping(value = "{key}", produces = APPLICATION_JSON_VALUE)
public ResponseEntity<UserDto> getSimpleDto(@PathVariable("key") String key) {
return ResponseEntity.ok(repository.get(key));
}
}
PointController: Управляет баллами пользователей. В этом контроллере реализован метод, который добавляет или удаляет баллы у пользователей.
PointContoller.java
@RestController
@RequestMapping("api/user/point")
public class PointController {
private final Map<String, UserDto> repository;
public PointController(Map<String, UserDto> repository) {
this.repository = repository;
}
@PostMapping("{key}")
public HttpStatus changePoints(
@PathVariable String key,
@RequestParam("point") Long point,
@RequestParam("type") String type
) {
final UserDto userDto = repository.get(key);
userDto.setPoints(
"plus".equalsIgnoreCase(type)
? userDto.getPoints() + point
: userDto.getPoints() - point
);
return HttpStatus.OK;
}
}
SecretController: Содержит метод destroy, который может удалить всех пользователей из системы.
SecretContoller.java
@RestController
@RequestMapping("api/secret")
public class SecretController {
private final Map<String, UserDto> repository;
public SecretController(Map<String, UserDto> repository) {
this.repository = repository;
}
@GetMapping(value = "destroy")
public HttpStatus destroy() {
repository.clear();
return HttpStatus.OK;
}
}
Настраиваем Swagger
Для добавления Swagger в проект необходимо добавить соответствующие зависимости в pom.xml:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>Актуальная версия в Maven Central: SpringDoc Starter
Если используется WebFlux, потребуется другая зависимость:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<version>2.6.0</version>
</dependency>Актуальная версия в Maven Central: SpringDoc Starter WebFlux
Эти зависимости предназначены для проектов на Spring Boot 3. Если ваш проект работает на Spring Boot 2, добавьте следующие зависимости:
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.25</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.8.0</version>
</dependency>После добавления зависимостей документация будет доступна по адресу: http://localhost:8080/swagger-ui/index.html, а спецификация OAS — по адресу: http://localhost:8080/v3/api-docs.
Swagger автоматически обнаруживает все контроллеры, отображая доступные HTTP-методы для каждого. Для каждого HTTP-метода доступна информация о статусе ответа, типе содержимого и параметрах.

Swagger также позволяет вызывать методы API через пользовательский интерфейс. Можно вызвать каждый метод с помощью пользовательского интерфейса.

Однако, по умолчанию, документация может выглядеть недостаточно информативно. Чтобы улучшить её, создадим конфигурационный класс Swagger — OpenApiConfig:
@OpenAPIDefinition(
info = @Info(
title = "Loyalty System Api",
description = "API системы лояльности",
version = "1.0.0",
contact = @Contact(
name = "Struchkov Mark",
email = "mark@struchkov.dev",
url = "https://mark.struchkov.dev"
)
)
)
public class OpenApiConfig {
// Конфигурация для Swagger
}title— название приложения;version— версия API;contact— информация об ответственных за API.
Эти данные добавляют визуальную привлекательность пользовательскому интерфейсу документации.
Разметка контроллеров
Чтобы сделать документацию понятнее, переопределим описания контроллеров, используя аннотацию @Tag:
@Tag(name = "Название контроллера", description = "Описание контроллера")
public class ControllerName {
// Контент контроллера
}
Кроме того, @Tag поддерживает параметр externalDocs, который позволяет добавить ссылку на внешнюю документацию, связанную с контроллером. Это может быть полезным, если API требует ссылки на общие технические документы, дополнительные спецификации или инструкции. Пример:
@Tag(
name = "User Controller",
description = "Контроллер для управления пользователями",
externalDocs = @ExternalDocumentation(
description = "Ссылка на общую документацию",
url = "https://example.com/docs/user-controller"
)
)
public class UserController {
// ... Контент контроллера ...
}Скрытие контроллера
Если нужно скрыть контроллер из документации Swagger, можно воспользоваться аннотацией @Hidden. Например, спрячем SecretController:
@Hidden
@Tag(name = "Секретный контролер", description = "Позволяет удалить всех пользователей")
public class SecretController {
// Контент контроллера
}
@Hidden скрывает элементы только из интерфейса Swagger, но не делает их недоступными для вызова. Для обеспечения безопасности используйте методы аутентификации и авторизации, такие как JWT-токены, OAuth2 или API-ключи, которые обеспечат доступ только для авторизованных пользователей.@Hidden позволяет скрыть из Swagger не только контроллеры целиком, но также отдельные эндпойнты.
@Hidden
@PutMapping(produces = APPLICATION_JSON_VALUE)
@Operation(summary = "Регистрация пользователя", description = "Позволяет зарегистрировать пользователя")
public HttpStatus registerUser(@RequestBody UserDto userDto) {
userDto.setPoints(0L);
repository.put(userDto.getKey(), userDto);
return HttpStatus.OK;
}
После добавления аннотаций документация становится гораздо понятнее. Теперь добавим описания для каждого метода контроллера.
Разметка методов
Для описания методов контроллеров используется аннотация @Operation. Она позволяет указать:
summary— краткое описание метода.description— более развернутое описание.
Пример аннотации метода для регистрации пользователя:
@Operation(
summary = "Регистрация пользователя",
description = "Позволяет зарегистрировать пользователя"
)
public HttpStatus registerUser(@RequestBody UserDto userDto) {
// Реализация метода
}

Разметка параметров метода
Для описания параметров эндпойнтов используем аннотацию @Parameter, которая позволяет уточнить детали каждого параметра.
public HttpStatus changePoints(
@PathVariable @Parameter(description = "Идентификатор пользователя", required = true) String key,
@RequestParam("point") @Parameter(description = "Количество баллов", example = "100") Long point,
@RequestParam("type") @Parameter(description = "Тип операции", example = "ADD") TypeOperation type
) {
// Реализация метода
}По умолчанию параметры считаются необязательными. С помощью параметра required в аннотации @Parameter можно указать обязательные поля для запроса.
Для более точного описания значений параметров можно использовать атрибут example. Этот атрибут помогает задать пример значения. Например, в этом коде example = "100" указывает пример значения для параметра point, а example = "PLUS" — для параметра type, что наглядно демонстрирует возможные значения, которые API ожидает получить.
Разметка DTO
Чтобы сделать документацию ещё более понятной, можно добавить человеко-понятные описания для самой DTO и её полей, используя аннотацию @Schema. Даже если переменные в классе названы понятно, дополнительные пояснения помогут пользователям API быстрее ориентироваться в данных.
Пример базовой разметки для UserDto выглядит так:
@Schema(description = "Сущность пользователя")
public class UserDto {
@Schema(description = "Уникальный идентификатор пользователя", example = "A-124523", accessMode = Schema.AccessMode.READ_ONLY)
private String key;
@Schema(description = "ФИО", example = "Иванов Иван Иванович")
private String name;
@Schema(description = "Баллы пользователя", example = "0", accessMode = Schema.AccessMode.READ_ONLY)
private Long points = 0L;
@Schema(description = "Дата регистрации пользователя", example = "2023-01-01", accessMode = Schema.AccessMode.READ_ONLY)
private LocalDate registrationDate;
@Schema(description = "Статус пользователя", allowableValues = {"ACTIVE", "INACTIVE", "BANNED"})
private String status;
// Вложенная структура для контактных данных
@Schema(description = "Контактные данные пользователя")
private ContactInfo contactInfo;
// ... другие поля ...
}Swagger автоматически определяет формат для стандартных типов данных, таких как enum и даты. Однако, для полей со специфичным форматом желательно добавлять примеры значений через параметр example. Например, в данном коде example = "A-124523" показывает, как может выглядеть key, а example = "0" — формат для поля points. Выглядеть это будет так:


В разделе Schema и при отправке запроса
Если в DTO есть поля, которые пользователю вводить не нужно , их можно скрыть из интерфейса ввода в Swagger с помощью accessMode = Schema.AccessMode.READ_ONLY. Это обозначает, что поле будет отображаться только для чтения и Swagger не будет запрашивать его при отправке запроса.
Для полей, имеющих ограниченные значения, например, status, можно использовать параметр allowableValues, который укажет пользователям API допустимые значения. Для enum allowableValues Swager сформирует сам.
Кроме того, если DTO содержит вложенные объекты (например, ContactInfo в UserDto), желательно также разметить вложенные классы с помощью @Schema, чтобы Swagger отобразил их структуру и описание всех уровней вложенности.
Валидация
О валидации данных я подробно рассказывал в статье “Валидация данных в Spring Boot”. Здесь же хочу показать, что при добавлении валидации для параметров методов контроллеров эти ограничения также автоматически отображаются в Swagger.
Добавим валидацию в метод управления баллами пользователя в PointController. Например, нам нужно, чтобы значение баллов не могло быть отрицательным. Для этого используем аннотацию @Min с минимальным значением 0:
public HttpStatus changePoints(
// ... другие параметры ...
@RequestParam("point") @Min(0) @Parameter(description = "Количество баллов") Long point
// ... другие параметры ...
) {
// Реализация метода
}Теперь, благодаря валидации, Swagger автоматически добавляет в спецификацию для поля point ограничение minimum: 0. Пользователи API сразу видят требование к этому параметру.

Можно также указать коды ошибок, которые возвращаются при нарушении правил валидации. Например, добавим описание ответа с кодом 400 (ошибка ввода) с помощью аннотации @ApiResponse:
@Operation(summary = "Изменение баллов пользователя")
@ApiResponse(responseCode = "400", description = "Неверный запрос — количество баллов должно быть неотрицательным")
public HttpStatus changePoints(
@RequestParam("point") @Min(value = 0, message = "Количество баллов не может быть отрицательным")
@Parameter(description = "Количество баллов") Long point
) {
// Реализация метода
}
Авторизация в Swager
Если ваш API защищён аутентификацией и авторизацией, вы не сможете просто так выполнять запросы из Swagger. Один из самых распространённых способов авторизации — это JWT, о котором я рассказывал в отдельной статье.
Чтобы Swagger мог корректно отправлять запросы к защищённым эндпойнтам, необходимо указать, что API использует JWT для авторизации, и задать, какие методы требуют токена.
Авторизация с использованием JWT
Для авторизации через JWT Swagger должен получать access-токен и автоматически добавлять его в заголовок Authorization каждого запроса. Начнём с описания схемы авторизации в конфигурационном классе Swagger, добавив аннотацию @SecurityScheme над классом OpenApiConfig:
@OpenAPIDefinition(...)
@SecurityScheme(
name = "JWT",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
scheme = "bearer"
)
public class OpenApiConfig {
}Здесь мы определили схему авторизации с использованием JWT, указав, что токен имеет формат bearer и будет использоваться в заголовке запроса. Теперь Swagger знает, что для авторизации потребуется токен JWT.
После этого помечаем аннотацией @SecurityRequirement все эндпойнты, которые требуют авторизации. В атрибуте name аннотации @SecurityRequirement указываем название схемы авторизации — в данном случае "JWT", что должно соответствовать значению, указанному в аннотации @SecurityScheme:
@PostMapping("{key}")
@SecurityRequirement(name = "JWT")
@Operation(summary = "Управление баллами", description = "Позволяет удалить или добавить баллы пользователю")
public HttpStatus changePoints(...) {
... ... ...
}Пример помеченного контроллера
Запустив приложение и открыв Swagger, мы увидим, что в интерфейсе появилась кнопка Authorize. Нажав на неё, можно ввести JWT-токен, который будет автоматически добавляться ко всем защищённым методам. Методы, защищённые аннотацией @SecurityRequirement, теперь будут отмечены значком замка.

После установки токена, перейдите к защищённому эндпойнту и вызовите его. Swagger добавит заголовок Authorization со значением токена и автоматически проставит перед ним Bearer, как того требует спецификация JWT.

Если токен истёк или стал недействителен, его необходимо ввести заново через кнопку Authorize. Swagger не поддерживает автоматическое обновление токенов, поэтому при использовании токенов с ограниченным сроком действия разработчикам потребуется самостоятельно получать и вводить новый токен.
Авторизация с использованием Ouath2
Работа с OAuth2 в Swagger несколько сложнее, чем с JWT, из-за особенностей авторизации в Spring Boot. При OAuth2-авторизации Spring Boot создаёт JSESSIONID — специальную куку, которая сохраняется в браузере и передаётся с каждым запросом. Это позволяет приложению идентифицировать пользователя.
Swagger при авторизации через OAuth2 генерирует access token, который он затем использует для запросов. Однако он не может сам получить JSESSIONID, так как его создаёт Spring Boot после успешной авторизации, и передаётся кука исключительно через браузер.
Swagger же при Oauth2 авторизации генерирует себе access token, который пытается использовать при запросе. Проблема в том, что он никак не может сам получить значение JSESSIONID, так как его генерирует Spring после успешной Oauth2 авторизации.
Чтобы использовать JSESSIONID для авторизации, укажем Swagger, что он должен передавать его в виде куки. Добавим аннотацию @SecurityScheme в конфигурационный класс OpenApiConfig, настроив её для передачи JSESSIONID в запросах:
@OpenAPIDefinition(...)
@SecurityScheme(
name = "jsessionid",
in = SecuritySchemeIn.COOKIE,
type = SecuritySchemeType.APIKEY,
paramName = "JSESSIONID"
)
public class OpenApiConfig {
}В этом примере мы указываем, что схема безопасности jsessionid использует куку JSESSIONID для передачи токена. Теперь Swagger знает, что для авторизации будет использоваться именно эта кука.
Затем помечаем защищённые эндпойнты аннотацией @SecurityRequirement, указав в атрибуте name название схемы — jsessionid:
@SecurityRequirement(name = "jsessionid")Для прохождения авторизации переходим на любой URL вашего API, требующий авторизации, и видим окно авторизации OAuth2. После успешного входа JSESSIONID будет сохранён в браузере.
Откройте консоль разработчика в браузере: Найдите раздел с куками и отыщите куку JSESSIONID.

Скопируйте значение JSESSIONID: Откройте окно авторизации в Swagger и вставьте значение JSESSIONID в соответствующее поле.

Теперь, когда JSESSIONID установлен, Swagger будет передавать его в каждом запросе к защищённым эндпойнтам, обеспечивая корректную аутентификацию.
Заключение
В этой статье мы рассмотрели, как настроить и документировать API в Spring Boot, используя Swagger для упрощённого взаимодействия с разработчиками и тестирования защищённых эндпойнтов. Мы начали с основ настройки Swagger и аннотаций для документирования контроллеров, DTO и параметров методов. Затем добавили валидацию данных, которая не только усиливает защиту приложения, но и помогает пользователям API понимать ограничения при работе с запросами.