Весенний Webflux с Kotlin
1. обзор
В этом руководстве мы покажем, как использовать модуль Spring WebFlux с использованием языка программирования Kotlin.
Мы иллюстрируем, как использовать подходы на основе аннотаций и лямбда-стилей при определении конечных точек.
2. Spring WebFlux и Kotlin
Релиз Spring 5 представил две новые важные функции, среди которых нативная поддержка парадигмы реактивного программирования и возможность использовать язык программирования Kotlin.
В этом руководстве мы предполагаем, что мы уже настроили среду (обратитесь кone of our tutorials по этому вопросу) и понимаем синтаксис языка Kotlin (another tutorial по теме).
3. Аннотационный подход
WebFlux позволяет нам определять конечные точки, которые должны обрабатывать входящие запросы известным способом, используя аннотации инфраструктуры SpringMVC, такие как@RequestMapping или@PathVariable, или удобные аннотации, такие как@RestController или@GetMappingс.
Несмотря на то, что имена аннотаций совпадают,WebFlux’s делают методыnon-blocking.
Например, эта конечная точка:
@GetMapping(path = ["/numbers"],
produces = [MediaType.APPLICATION_STREAM_JSON_VALUE])
@ResponseBody
fun getNumbers() = Flux.range(1, 100)
производит поток некоторых первых целых чисел. Если сервер работает наlocalhost:8080, то подключитесь к нему с помощью команды:
curl localhost:8080/stream
распечатает запрошенные номера.
4. Лямбда на основе подхода
Более новый подход в определении конечных точек заключается в использовании лямбда-выражений, которые присутствуют в Java начиная с версии 1.8. С помощью Kotlin лямбда-выражения можно использовать даже с более ранними версиями Java.
В WebFlux функции маршрутизатора - это функции, определяемыеRequestPredicate (другими словами, кто должен управлять запросом) иHandlerFunction (другими словами, как должен быть разработан запрос).
Функция-обработчик принимает экземплярServerRequest и создает экземплярMono<ServerResponse>.
В Котлине if the last function argument is a lambda expression, it can be placed outside the parentheses.
Такой синтаксис позволяет выделить разделение между предикатом запроса и функцией-обработчиком.
router {
GET("/route") { _ -> ServerResponse.ok().body(fromObject(arrayOf(1, 2, 3))) }
}
для функций роутера.
Функция имеет понятный для человека формат: как только запрос типа GET поступит в / route, затем создаст ответ (игнорируя содержимое запроса - отсюда символ подчеркивания) с HTTP-статусом OK и с телом, созданным из данный объект.
Теперь, чтобы заставить его работать в WebFlux, мы должны поместить функцию router в класс:
@Configuration
class SimpleRoute {
@Bean
fun route() = router {
GET("/route") { _ -> ServerResponse.ok().body(fromObject(arrayOf(1, 2, 3))) }
}
}
Зачастую логика наших приложений требует, чтобы мы создавали более сложные функции маршрутизатора.
В WebFlux функция DSL маршрутизатора Kotlin определяет множество функций, таких какaccept,and,or,nest,invoke,GET,POST с помощьюextension functions, который позволяет нам создавать составные функции маршрутизатора:
router {
accept(TEXT_HTML).nest {
(GET("/device/") or GET("/devices/")).invoke(handler::getAllDevices)
}
}
Переменнаяhandler должна быть экземпляром класса, реализующего методgetAllDevices() со стандартной сигнатуройHandlerFunction:
fun getAllDevices(request: ServerRequest): Mono
как мы уже упоминали выше.
Чтобы обеспечить надлежащее разделение задач, мы можем поместить определения несвязанных функций маршрутизатора в отдельные классы:
@Configuration
class HomeSensorsRouters(private val handler: HomeSensorsHandler) {
@Bean
fun roomsRouter() = router {
(accept(TEXT_HTML) and "/room").nest {
GET("/light", handler::getLightReading)
POST("/light", handler::setLight)
}
}
// eventual other router function definitions
}
Мы можем получить доступ к переменным пути с помощьюString-значного методаpathVariable():
val id = request.pathVariable("id")
а доступ к телуServerRequest достигается с помощью методовbodyToMono иbodyToFlux, то есть:
val device: Mono = request
.bodyToMono(Device::class.java)
5. тестирование
Чтобы протестировать функции маршрутизатора, мы должны создать экземплярWebTestClient для маршрутизаторовSimpleRoute().route(), которые мы хотим протестировать:
var client = WebTestClient.bindToRouterFunction(SimpleRoute().route()).build()
Теперь мы готовы проверить, возвращает ли функция-обработчик маршрутизатора статус OK:
client.get()
.uri("/route")
.exchange()
.expectStatus()
.isOk
Интерфейс WebTestClient определяет методы, которые позволяют нам тестировать все методы HTTP-запроса, такие какGET,POST иPUT, даже без запуска сервера.
Чтобы проверить содержимое тела ответа, мы можем использовать методjson():
client.get()
.uri("/route")
.exchange()
.expectBody()
.json("[1, 2, 3]")
6. Заключение
В этой статье мы продемонстрировали, как использовать основные функции платформы WebFlux с помощью Kotlin.
Мы кратко упомянули хорошо известный подход, основанный на аннотациях, при определении конечных точек и посвятили больше времени, чтобы проиллюстрировать, как определить их с помощью функций маршрутизатора в стиле, основанном на лямбда-выражениях.
Все фрагменты кода можно найти в нашем репозиторииover on GitHub.