Перейти к основному содержимому

Настройка Swagger 2 с помощью Spring REST API с использованием Springfox

· 11 мин. чтения

1. Обзор

В настоящее время внешние и внутренние компоненты часто разделяют веб-приложение. Обычно мы предоставляем API в качестве внутреннего компонента для внешнего компонента или интеграции сторонних приложений.

В таком сценарии важно иметь правильные спецификации для серверных API. В то же время документация по API должна быть информативной, читабельной и простой для понимания.

Более того, справочная документация должна одновременно описывать каждое изменение в API. Выполнять это вручную — утомительное занятие, поэтому автоматизация процесса была неизбежна.

В этом руководстве мы рассмотрим Swagger 2 для веб-службы Spring REST , используя реализацию Springfox спецификации Swagger 2. Важно отметить, что последняя версия спецификации Swagger, теперь известная как OpenAPI 3.0, лучше поддерживается проектом Springdoc и должна использоваться для документирования Spring REST API .

Если вы не знакомы со Swagger, посетите его веб-страницу , чтобы узнать больше, прежде чем продолжить работу с этим руководством.

2. Целевой проект

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

3. Добавление зависимости Maven

Как упоминалось выше, мы будем использовать реализацию Springfox спецификации Swagger. Последнюю версию можно найти на Maven Central .

Чтобы добавить его в наш проект Maven, нам нужна зависимость в файле pom.xml :

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>

3.1. Весенняя загрузочная зависимость

Для проектов на основе Spring Boot достаточно добавить одну зависимость springfox-boot-starter :

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

Мы можем добавить любые другие стартеры, которые нам нужны, с версией, управляемой родителем Spring Boot:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
</dependency>

4. Интеграция Swagger 2 в проект

4.1. Конфигурация Java

Конфигурация Swagger в основном сосредоточена вокруг компонента Docket :

@Configuration
public class SpringFoxConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}

После определения bean- компонента Docket его метод select() возвращает экземпляр ApiSelectorBuilder , который предоставляет способ управления конечными точками, предоставляемыми Swagger.

Мы можем настроить предикаты для выбора RequestHandler с помощью RequestHandlerSelectors и PathSelectors . Использование any() для обоих сделает документацию для всего нашего API доступной через Swagger.

4.2. Конфигурация без загрузки Spring

В простых проектах Spring нам нужно явно включить Swagger 2. Для этого мы должны использовать @EnableSwagger2WebMvc в нашем классе конфигурации :

@Configuration
@EnableSwagger2WebMvc
public class SpringFoxConfig {
}

Кроме того, без Spring Boot у нас нет такой роскоши, как автоматическая настройка наших обработчиков ресурсов.

Пользовательский интерфейс Swagger добавляет набор ресурсов, которые мы должны настроить как часть класса, расширяющего WebMvcConfigurerAdapter и снабженного аннотацией @EnableWebMvc:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");

registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}

4.3. Проверка

Чтобы убедиться, что Springfox работает, мы можем посетить этот URL-адрес в нашем браузере:

http://localhost:8080/spring-security-rest/api/v2/api-docs

Результатом является ответ JSON с большим количеством пар ключ-значение, который не очень удобочитаем. К счастью, Swagger предоставляет для этой цели пользовательский интерфейс Swagger .

5. Сваггерский пользовательский интерфейс

Swagger UI — это встроенное решение, которое значительно упрощает взаимодействие пользователя с созданной Swagger документацией по API.

5.1. Включение пользовательского интерфейса Springfox Swagger

Чтобы использовать пользовательский интерфейс Swagger, нам нужно добавить дополнительную зависимость Maven:

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

Теперь мы можем протестировать его в нашем браузере, посетив:

http://localhost:8080/ваш-приложение-root/swagger-ui/

В нашем случае, кстати, точный URL будет таким:

http://localhost:8080/spring-security-rest/api/swagger-ui/

Результат должен выглядеть примерно так:

./8015158d54c1a704e2845cad0fefbbe8.png

5.2. Изучение документации Swagger

В ответе Swagger есть список всех контроллеров, определенных в нашем приложении. При нажатии на любой из них будут перечислены допустимые методы HTTP ( DELETE , GET , HEAD , OPTIONS , PATCH , POST , PUT ).

Расширение каждого метода предоставляет дополнительные полезные данные, такие как статус ответа, тип содержимого и список параметров. Также можно попробовать каждый метод с помощью пользовательского интерфейса.

Возможность синхронизации Swagger с нашей кодовой базой имеет решающее значение. Чтобы продемонстрировать это, мы можем добавить новый контроллер в наше приложение:

@RestController
public class CustomController {

@RequestMapping(value = "/custom", method = RequestMethod.POST)
public String custom() {
return "custom";
}
}

Теперь, если мы обновим документацию Swagger, мы увидим custom-controller в списке контроллеров. Как мы знаем, в ответе Swagger показан только один метод ( POST ).

6. Весенние данные REST

Springfox обеспечивает поддержку Spring Data REST через свою библиотеку springfox-data-rest .

Spring Boot позаботится об автоматической настройке, если обнаружит spring-boot-starter-data-rest в пути к классам .

Теперь давайте создадим объект с именем User :

@Entity
public class User {
@Id
private Long id;
private String firstName;
private int age;
private String email;

// getters and setters
}

Затем мы создадим UserRepository , чтобы добавить операции CRUD для объекта User :

@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}

Наконец, мы импортируем класс SpringDataRestConfiguration в класс SpringFoxConfig :

@EnableSwagger2WebMvc
@Import(SpringDataRestConfiguration.class)
public class SpringFoxConfig {
//...
}

Примечание. Мы использовали аннотацию @EnableSwagger2WebMvc для включения Swagger, поскольку она заменила аннотацию @EnableSwagger2 в версии 3 библиотек.

Давайте перезапустим приложение, чтобы сгенерировать спецификации для Spring Data REST API:

./3d0ceb2d03557a3d626eb7ee6486f6c1.jpg

Мы видим, что Springfox сгенерировал спецификации для объекта User с такими HTTP-методами, как GET , POST, PUT, PATCH и DELETE.

7. Проверка компонентов

Springfox также поддерживает аннотации проверки bean -компонентов через свою библиотеку springfox-bean-validators .

Во-первых, мы добавим зависимость Maven в наш pom.xml :

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.9.2</version>
</dependency>

Опять же, если мы используем Spring Boot, нам не нужно явно указывать вышеуказанную зависимость .

Затем давайте добавим несколько аннотаций проверки, таких как @NotNull и @Min , к сущности пользователя :

@Entity
public class User {
//...

@NotNull(message = "First Name cannot be null")
private String firstName;

@Min(value = 15, message = "Age should not be less than 15")
@Max(value = 65, message = "Age should not be greater than 65")
private int age;
}

Наконец, мы импортируем класс BeanValidatorPluginsConfiguration в класс SpringFoxConfig :

@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SpringFoxConfig {
//...
}

Давайте посмотрим на изменения в спецификациях API:

./62c21f59770d4135e4d7aa2804c7d659.jpg

Здесь мы можем заметить, что модель User требует * для firstName . Также для возраста определены минимальное и максимальное значения . ``

8. Плагин

Чтобы добавить определенные функции в спецификации API, мы можем создать плагин Springfox. Плагин может предлагать различные функции, от обогащения моделей и свойств до пользовательских списков API и значений по умолчанию.

Springfox поддерживает создание плагинов через свой модуль spi . Модуль spi предоставляет несколько интерфейсов, таких как ModelBuilderPlugin , ModelPropertyBuilderPlugin и ApiListingBuilderPlugin , которые действуют как крючок расширения для реализации пользовательского плагина.

Чтобы продемонстрировать возможности, давайте создадим подключаемый модуль для обогащения свойства электронной почты модели User . Мы будем использовать интерфейс ModelPropertyBuilderPlugin и установим значения шаблона и примера .

Во-первых, давайте создадим класс EmailAnnotationPlugin и переопределим метод supports , чтобы разрешить любой тип документации , такой как Swagger 1.2 и Swagger 2:

@Component
@Order(Validators.BEAN_VALIDATOR_PLUGIN_ORDER)
public class EmailAnnotationPlugin implements ModelPropertyBuilderPlugin {
@Override
public boolean supports(DocumentationType delimiter) {
return true;
}
}

Затем мы переопределим метод применения модуля ModelPropertyBuilderPlugin , чтобы установить значения свойств построителя:

@Override
public void apply(ModelPropertyContext context) {
Optional<Email> email = annotationFromBean(context, Email.class);
if (email.isPresent()) {
context.getSpecificationBuilder().facetBuilder(StringElementFacetBuilder.class)
.pattern(email.get().regexp());
context.getSpecificationBuilder().example("email@email.com");
}
}

Таким образом, спецификации API будут показывать шаблон и примеры значений свойства, аннотированного аннотацией @Email .

Далее мы добавим аннотацию @Email к сущности User :

@Entity
public class User {
//...

@Email(regexp=".*@.*\\..*", message = "Email should be valid")
private String email;
}

Наконец, мы включим EmailAnnotationPlugin в классе SpringFoxConfig , зарегистрировавшись как bean-компонент:

@Import({BeanValidatorPluginsConfiguration.class})
public class SpringFoxConfig {
//...

@Bean
public EmailAnnotationPlugin emailPlugin() {
return new EmailAnnotationPlugin();
}
}

Давайте проверим EmailAnnotationPlugin в действии:

./f46cc291d6546edb39eb3bffcb202ead.jpg

Мы видим, что значением шаблона является то же самое регулярное выражение (.@.\..*) из свойства электронной почты объекта User .

Точно так же значение примера ( email@email.com ) такое же, как определено в методе применения модуля EmailAnnotationPlugin .

9. Расширенная конфигурация

Bean - компонент Docket нашего приложения можно настроить, чтобы дать нам больший контроль над процессом создания документации API.

9.1. API фильтрации для ответа Swagger

Не всегда желательно раскрывать документацию для всего API. Мы можем ограничить ответ Swagger, передав параметры методам apis() и paths() класса Docket .

Как показано выше, RequestHandlerSelectors позволяет использовать предикаты any или none , но также может использоваться для фильтрации API в соответствии с базовым пакетом, аннотацией класса и аннотацией метода.

PathSelectors обеспечивает дополнительную фильтрацию с помощью предикатов, которые сканируют пути запросов нашего приложения. Мы можем использовать any() , none(), regex() или ant() .

В приведенном ниже примере мы укажем Swagger включать только контроллеры из определенного пакета с определенными путями, используя предикат ant() :

@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.foreach.web.controller"))
.paths(PathSelectors.ant("/foos/*"))
.build();
}

9.2. Пользовательская информация

Swagger также предоставляет в своем ответе некоторые значения по умолчанию, которые мы можем настроить, например «Документация по API», «Создано контактным адресом электронной почты» и «Apache 2.0».

Чтобы изменить эти значения, мы можем использовать метод apiInfo(ApiInfo apiInfo) — класс ApiInfo , который содержит пользовательскую информацию об API:

@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controller"))
.paths(PathSelectors.ant("/foos/*"))
.build()
.apiInfo(apiInfo());
}

private ApiInfo apiInfo() {
return new ApiInfo(
"My REST API",
"Some custom description of API.",
"API TOS",
"Terms of service",
new Contact("John Doe", "www.example.com", "myeaddress@company.com"),
"License of API", "API license URL", Collections.emptyList());
}

9.3. Ответные сообщения пользовательских методов

Swagger позволяет глобально переопределять ответные сообщения методов HTTP с помощью метода globalResponses() класса Docket . ``

Во-первых, нам нужно указать Swagger не использовать ответные сообщения по умолчанию. Предположим, мы хотим переопределить ответные сообщения 500 и 403 для всех методов GET .

Для этого необходимо добавить некоторый код в блок инициализации Docket (исходный код исключен для ясности):

.useDefaultResponseMessages(false)
.globalResponses(HttpMethod.GET, newArrayList(
new ResponseBuilder().code("500")
.description("500 message").build(),
new ResponseBuilder().code("403")
.description("Forbidden!!!!!").build()
));

./6e7cdac377054e2cde387f1f42ad01d9.png

10. Пользовательский интерфейс Swagger с API с защитой OAuth

Пользовательский интерфейс Swagger предоставляет ряд очень полезных функций, которые мы уже хорошо рассмотрели здесь. Но мы не можем использовать большинство из них, если наш API защищен и недоступен.

Давайте посмотрим, как мы можем позволить Swagger получить доступ к API, защищенному OAuth, используя тип предоставления кода авторизации в этом примере.

Мы настроим Swagger для доступа к нашему защищенному API, используя поддержку SecurityScheme и SecurityContext :

@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()));
}

10.1. Конфигурация безопасности

Мы определим bean-компонент SecurityConfiguration в нашей конфигурации Swagger и установим некоторые значения по умолчанию:

@Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.scopeSeparator(" ")
.useBasicAuthenticationWithAccessCodeGrant(true)
.build();
}

10.2. Схема безопасности

Далее мы определим нашу SecurityScheme ; это используется для описания того, как наш API защищен (базовая аутентификация, OAuth2, …).

В нашем случае здесь мы определим схему OAuth, используемую для защиты нашего сервера ресурсов :

private SecurityScheme securityScheme() {
GrantType grantType = new AuthorizationCodeGrantBuilder()
.tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/token", "oauthtoken"))
.tokenRequestEndpoint(
new TokenRequestEndpoint(AUTH_SERVER + "/authorize", CLIENT_ID, CLIENT_SECRET))
.build();

SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}

Обратите внимание, что мы использовали тип предоставления кода авторизации, для которого нам необходимо указать конечную точку маркера и URL-адрес авторизации нашего сервера авторизации OAuth2.

И вот области, которые нам нужно определить:

private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("read", "for read operations"),
new AuthorizationScope("write", "for write operations"),
new AuthorizationScope("foo", "Access foo API") };
return scopes;
}

Они синхронизируются с областями, которые мы фактически определили в нашем приложении для /foos API.

10.3. SecurityContext

Наконец, нам нужно определить SecurityContext для нашего примера API:

private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(
Arrays.asList(new SecurityReference("spring_oauth", scopes())))
.forPaths(PathSelectors.regex("/foos.*"))
.build();
}

Обратите внимание, как имя, которое мы использовали здесь в справочнике — spring_oauth — синхронизируется с именем, которое мы использовали ранее в SecurityScheme .

10.4. Тест

Теперь, когда у нас все настроено и готово к работе, давайте взглянем на наш пользовательский интерфейс Swagger и попробуем получить доступ к Foo API.

Мы можем получить доступ к пользовательскому интерфейсу Swagger локально:

http://localhost:8082/spring-security-oauth-resource/swagger-ui.html

Как мы видим, новая кнопка авторизации теперь существует из-за наших настроек безопасности:

./8f51747ec401a9b45a5affcfb9756736.png

Когда мы нажимаем кнопку «Авторизовать», мы видим следующее всплывающее окно для авторизации нашего пользовательского интерфейса Swagger для доступа к защищенному API:

./eb4e9106051efbda4bda4e252e4c64f9.png

Обратите внимание, что:

  • Мы уже можем видеть CLIENT_ID и CLIENT_SECRET, поскольку мы предварительно настроили их ранее (но мы все еще можем их изменить).
  • Теперь мы можем выбрать нужные нам области видимости.

Вот как помечается защищенный API:

./26f79d4ec5f398e1d1e3307d0f72c8ba.png

И вот, наконец, мы можем задействовать наш API!

Конечно, почти само собой разумеется, что теперь, когда эта конфигурация безопасности активна, нам нужно быть осторожными с тем, как мы раскрываем пользовательский интерфейс Swagger извне.

11. Заключение

В этой статье мы настроили Swagger 2 для создания документации для Spring REST API. Мы также изучили способы визуализации и настройки вывода Swagger. И, наконец, мы рассмотрели простую конфигурацию OAuth для Swagger.

Полную реализацию этого туториала можно найти в проекте GitHub . Чтобы увидеть настройку в проекте Boot, ознакомьтесь с этим модулем GitHub .

Для раздела OAuth код доступен в нашем репозитории spring-security-oauth .

А если вы изучаете REST с Spring , перейдите к уроку 1 модуля 7, чтобы подробно изучить настройку Swagger с помощью Spring и Spring Boot.