Реактивный REST контроллер в Spring
В spring boot, начиная с версии 2.0, добавили возможность использовать реактивный подход для некоторых сценариев. Например, стало возможным использовать реактивные драйверы для некоторых баз данных и для REST контроллеров. В сети полно примеров, где показано как реализовать REST контроллер самому. Однако, бывают ситуации, когда контроллер и модель должны удовлетворять какому-либо контракту, например, описанному с помощью open api. В этой статье я покажу, как сгенерировать реактивный контроллер при помощи openapi-generator-maven-plugin.
Сгененрируем скелет проекта с помощью spring initializr. В своем проекте я использовал maven, java 11, spring boot 2.1.0 и зависимость на reactive web. В качестве дополнительной зависимости придется добавить swagger-annotations, чтобы компиляция сгенерированных классов не приводила к ошибке.
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
Подготовим описание простого REST итерфейса с помощью open api.
openapi: 3.0.1
info:
description: 'Simple open api file'
title: 'Simple open api file'
version: 1.0.0
servers:
- url: http://localhost:8080/v1
paths:
/hello:
get:
description: hello world!
operationId: helloWorld
responses:
200:
content:
application/json:
schema:
type: string
description: successful operation
А теперь настроим openapi-generator-maven-plugin.
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>3.3.2</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/swagger/simple-open-api.yaml</inputSpec>
<output>${generated.sources.basedir}</output>
<generatorName>spring</generatorName>
<apiPackage>${default.package}.controller</apiPackage>
<ignoreFileOverride>${project.basedir}/.openapi-generator-ignore</ignoreFileOverride>
<configOptions>
<delegatePattern>true</delegatePattern>
<reactive>true</reactive>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
Коротко опишу используемые ключи:
- inputSpec - путь до yaml файла с описанием контракта.
- output - директория, где будут расположены генерируемые классы.
- generatorName - имя генератора, с помощью которого будут генерироваться классы.
- apiPackage - путь, по которым будут доступны сгенерированы классы для API.
- ignoreFileOverride - путь до ignore файла, где можно указать, какие классы нужно исключить из кодогенерации.
- configOptions.delegatePattern - использование класса делегата для API. Эта опция помогает скрыть swagger аннотации и сосредоточиться на логике.
- configOptions.reactive - использует реактивное API для возвращаемых результатов.
Теперь самое время показать наш контроллер, который будет реализовывать сгенерированный интерфейс.
@Controller
public class HelloController implements HelloApiDelegate {
@Override
public Mono<ResponseEntity<String>> helloWorld(ServerWebExchange exchange) {
return Mono.just(
ResponseEntity.ok("World!")
);
}
}
Вот и все! Полный код доступен по ссылке