{"id":400317,"date":"2024-06-29T15:11:24","date_gmt":"2024-06-29T15:11:24","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=400317"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=400317","title":{"rendered":"<span>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0445 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Spring WebFlux, Kotlin \u0438 Coroutines<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0435\u0442 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441, \u00ab\u043e\u0431\u044b\u0447\u043d\u043e\u0435\u00bb \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0438\u0437 \u043f\u0443\u043b\u0430 \u043f\u043e\u0442\u043e\u043a\u043e\u0432.\u00a0\u042d\u0442\u043e\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u043e\u0442\u0432\u0435\u0442 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d \u0432 \u0441\u043e\u043a\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.\u00a0\u041f\u043e\u043f\u0443\u0442\u043d\u043e \u043d\u0430\u043c \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u0435\u0431-\u0441\u043b\u0443\u0436\u0431\u044b \u0438\u043b\u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0438\u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u0444\u0430\u0439\u043b \u0438\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430, \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043e\u0442\u043e\u043a \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u043e\u0442\u0432\u0435\u0442.\u00a0\u0414\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u043e\u0442\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u0443\u043b \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0432 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u0441\u0447\u0435\u0440\u043f\u0430\u0442\u044c\u0441\u044f, \u0438 \u0442\u043e\u0433\u0434\u0430 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.\u00a0\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u0443\u043b \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0438 \u043c\u043e\u0434\u0435\u043b\u044c \u00ab\u043f\u043e\u0442\u043e\u043a \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u00bb, \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u043c\u0435\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u043f\u043e\u0442\u043e\u043a \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u0435 \u044f\u0434\u0440\u043e \u200b\u200b\u0426\u041f, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0438, \u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430, \u043e\u043d\u043e \u0440\u0430\u0437\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u044d\u0442\u0443 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430\u0434 \u0447\u0435\u043c-\u0442\u043e \u0435\u0449\u0435 \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 IO \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e.\u00a0\u041c\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u043c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0435\u0435.\u00a0<\/p>\n<p>\u041f\u043e\u0434\u0445\u043e\u0434 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0433\u0440\u0443\u043f\u043f\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043b\u0430\u0441\u044c \u0432 \u0438\u043d\u0438\u0446\u0438\u0430\u0442\u0438\u0432\u0435 Reactive Streams, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0438 \u0447\u0435\u0442\u044b\u0440\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 JVM.\u00a0\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u043d\u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u043e\u0448\u043b\u0438 \u0441\u0432\u043e\u0438\u043c \u043f\u0443\u0442\u0435\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u044d\u0442\u0438\u0445 \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0439.\u00a0\u041e\u0434\u043d\u0430 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, Project Reactor, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043e\u0439, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 Spring \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0432\u0435\u0431-\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 Spring WebFlux.\u00a0<\/p>\n<p>\u042d\u0442\u043e\u0442 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u0442\u0435\u043a \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0437\u043d\u0430\u043a\u043e\u043c\u043e\u0439 \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0438 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439, \u0435\u0441\u043b\u0438 \u0432\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441 Spring MVC, \u043d\u043e \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043a\u0440\u0438\u0432\u043e\u0439 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f.\u00a0<\/p>\n<p>\u0412\u0437\u0433\u043b\u044f\u043d\u0438\u0442\u0435 \u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043d\u0438\u0436\u0435.<\/p>\n<pre><code class=\"kotlin\">@RestController @RequestMapping(\"\/cats\") class CatController(     @Autowired val catRepository: CatRepository ) {     @PutMapping(\"{id}\")     fun updateProduct(@PathVariable(value = \"id\") id: Long, @RequestBody newCat: Cat): Mono&lt;ResponseEntity&lt;Cat?>> {         return catRepository             .findById(id)             .flatMap { existingCat ->                 catRepository.save(                     existingCat.copy(                         name = newCat.name,                         type = newCat.type,                         age = newCat.age                     )                 )             }             .map { updatedCat -> ResponseEntity.ok(updatedCat) }             .defaultIfEmpty(ResponseEntity.notFound().build())     } }<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043f\u043e\u043b\u044e\u0431\u0438\u0432\u0448\u0438\u0439\u0441\u044f \u043d\u0430\u043c \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c \u0437\u0430\u043c\u0435\u043d\u0435\u043d \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u0434\u0438\u0433\u043c\u043e\u0439 \u0441 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e\u043c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0442\u043e\u0440\u043e\u0432.\u00a0\u041d\u0430\u0448 \u043a\u043e\u0434 \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0446\u0435\u043f\u043e\u0447\u0435\u043a \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0442 \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430\u043c.\u00a0\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0434\u0435\u043b\u043e \u0441 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f\u043c\u0438, \u0442\u0430\u043a\u0438\u043c\u0438 \u043a\u0430\u043a Mono \u0438 Flux, \u043d\u0430 \u0432\u0441\u0435\u0445 \u0443\u0440\u043e\u0432\u043d\u044f\u0445 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0438, \u043a\u0430\u043a \u0442\u0430\u043a\u043e\u0432\u0430\u044f, \u043d\u0430\u0448\u0430 \u043a\u043e\u0434\u043e\u0432\u0430\u044f \u0431\u0430\u0437\u0430 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0439 \u0441 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c.\u00a0\u0412 \u043a\u043e\u043d\u0446\u0435 \u043a\u043e\u043d\u0446\u043e\u0432, \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u0441\u0435, \u0447\u0442\u043e \u043c\u044b \u0437\u043d\u0430\u0435\u043c, \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u0432\u043a\u0443\u0441\u043d\u043e\u0441\u0442\u0438.\u00a0\u041a\u0430\u0436\u0435\u0442\u0441\u044f, \u044d\u0442\u043e \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0446\u0435\u043d\u0430, \u043c\u043e\u0436\u0435\u043c \u043b\u0438 \u043c\u044b \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u0438\u043d\u0430\u0447\u0435?<\/p>\n<p>\u0414\u0430 \u043c\u044b \u043c\u043e\u0436\u0435\u043c!\u00a0Kotlin \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u044f\u0437\u044b\u043a\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u0443\u044e Coroutines (\u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u0443\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043e\u0431\u043e\u0439 \u043b\u0435\u0433\u043a\u0438\u0435 \u043f\u043e\u0442\u043e\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0430, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043a\u043e\u0434 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a, \u043a\u0430\u043a \u043c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u043a \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u043c\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044e.\u00a0\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043d\u0435 \u043f\u043e\u0434\u0432\u0435\u0440\u0433\u0430\u044f \u043d\u0430\u0448\u0443 \u043a\u043e\u0434\u043e\u0432\u0443\u044e \u0431\u0430\u0437\u0443 \u043f\u043e\u043b\u043d\u043e\u0439 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0440\u0430\u0431\u043e\u0442\u043a\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0430\u0448\u0438 \u0434\u0440\u0443\u0437\u044c\u044f \u0438\u0437 Spring \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043b\u0438 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0440\u043e\u0448\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Kotlin, \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043a\u043e\u0440\u0443\u0442\u0438\u043d, \u0432 \u0441\u0432\u043e\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443.\u00a0\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b\u00a0\u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043e\u0431\u0449\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0433\u043e API, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043c\u044b \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Reactor \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c.<\/p>\n<p>\u0417\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b\u0438\u0441\u044c?\u00a0\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0432\u0435\u0431-API \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Spring WebFlux \u0438 \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f Kotlin \u0432 Spring WebFlux.\u00a0\u042d\u0442\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u00a0<code>coRouter<\/code> (\u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0430, &#8216;co&#8217; \u0434\u043b\u044f \u043a\u043e\u0440\u0443\u0442\u0438\u043d) DSL \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0432 \u043d\u0430\u0448 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 <code>Flow<\/code>, \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b WebClient \u0441 &#8216;await&#8217;.<br \/>\u0412\u043c\u0435\u0441\u0442\u043e \u043c\u043e\u0434\u0435\u043b\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 <code>@RestController<\/code>, <code>@RequestMapping<\/code> \u0438 <code>@Service<\/code> \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0431-\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0443\u044e \u043d\u043e\u0432\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 (Router) \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (Handler).<\/p>\n<p>\u041c\u044b \u0441\u043b\u0435\u0434\u0443\u0435\u043c \u0437\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0438\u0437\u0432\u043d\u0435.\u00a0\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043e\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430\u00a0<code>@RequestMapping<\/code>.\u00a0\u0417\u0430\u0442\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c.\u00a0\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043c\u044b \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043c \u043a \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u043e\u043c\u0443 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e.\u00a0\u042d\u0442\u043e\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0441\u043b\u043e\u0439 \u0438\u043c\u0435\u0435\u0442 \u0442\u043e \u0436\u0435 \u0438\u043c\u044f, \u0447\u0442\u043e \u0438 \u0432 Spring MVC, \u043d\u043e \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f, \u043b\u0435\u0436\u0430\u0449\u0430\u044f \u0432 \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u0435, \u0441\u043e\u0432\u0441\u0435\u043c \u0434\u0440\u0443\u0433\u0430\u044f, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u043c.\u00a0\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0438\u0445 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u0442\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f WebFlux, \u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0441\u0442\u0430\u0442\u0435\u0439 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u044d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434.\u00a0\u041e\u0434\u043d\u0430\u043a\u043e \u044f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0438\u0434\u0438\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0434\u043b\u044f Kotlin.\u00a0\u0412\u0441\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043a\u043e\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432\u00a0<a href=\"https:\/\/github.com\/BjornvdLaan\/spring-boot-webflux-kotlin-h2-example\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 GitHub<\/a>.<\/p>\n<h3>\u041d\u0430\u0448 \u043f\u0440\u0438\u043c\u0435\u0440<\/h3>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c API \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 CMS (Cat Management System), \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u0447\u0442\u0435\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f (CRUD).\u00a0\u041d\u0438\u0436\u0435 \u0432\u044b \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043e\u0431\u0437\u043e\u0440 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c, \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th data-colwidth=\"124\" width=\"124\">\n<p><strong>HTTP <\/strong>\u043c\u0435\u0442\u043e\u0434<\/p>\n<\/th>\n<th data-colwidth=\"111\" width=\"111\">\n<p><strong>Route<\/strong><\/p>\n<\/th>\n<th data-colwidth=\"147\" width=\"147\">\n<p><strong>HTTP <\/strong>\u0421\u0442\u0430\u0442\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 <\/p>\n<\/th>\n<th>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>GET<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>200 Ok<\/p>\n<\/td>\n<td>\n<p>\u0412\u0441\u0435 cat \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u044b<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>GET<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>200 Ok<\/p>\n<\/td>\n<td>\n<p>\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f cat \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>GET<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>404 Not Found<\/p>\n<\/td>\n<td>\n<p>\u0417\u0430\u043f\u0440\u0430\u0448\u0435\u043d cat \u0441 \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>POST<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>201 Created<\/p>\n<\/td>\n<td>\n<p>\u041d\u043e\u0432\u044b\u0439 cat \u0441\u043e\u0437\u0434\u0430\u043d \u0443\u0441\u043f\u0435\u0448\u043d\u043e<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>POST<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>400 Bad Request<\/p>\n<\/td>\n<td>\n<p>\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 cat<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>PUT<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>200 Ok<\/p>\n<\/td>\n<td>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 cat \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>PUT<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>400 Bad Request<\/p>\n<\/td>\n<td>\n<p>Cat \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>PUT<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>404 Not Found<\/p>\n<\/td>\n<td>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e cat <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>DELETE<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>204 No Content<\/p>\n<\/td>\n<td>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 cat \u0442\u0435\u043f\u0435\u0440\u044c \u0443\u0434\u0430\u043b\u0435\u043d<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>DELETE<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>404 Not Found<\/p>\n<\/td>\n<td>\n<p>\u0417\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e cat <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Spring Boot \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">Spring Initializr<\/a>.\u00a0\u041c\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u043f\u0440\u043e\u0435\u043a\u0442 Gradle, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0439 Kotlin \u0438 \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432 \u0432\u0438\u0434\u0435 jar-\u0444\u0430\u0439\u043b\u0430.\u00a0\u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438: Spring Reactive Web, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 WebFlux \u0438 Netty, Spring Data R2DBC \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u0438 H2 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/797\/170\/85d\/79717085d1abcfeef0505da8ff043f24.png\" width=\"700\" height=\"442\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/797\/170\/85d\/79717085d1abcfeef0505da8ff043f24.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0417\u0430\u0442\u0435\u043c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0435 \u0434\u0432\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f Mockk \u0438 SpringMockk:<\/p>\n<pre><code class=\"cmake\">dependencies {     ...     testImplementation(\"io.mockk:mockk:1.10.2\")     testImplementation(\"com.ninja-squad:springmockk:3.0.1\")     ... }<\/code><\/pre>\n<p>\u041e\u043d\u0438 \u043d\u0435 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u0442\u0440\u043e\u0433\u043e \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445.\u00a0\u042f \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0432\u0430\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c Maven Central \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u0439.<\/p>\n<h3>Router<\/h3>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 Router (\u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440) \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0442\u0438\u043f\u0430\u00a0<code>ServerRequest<\/code> \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 <code>ServerResponse<\/code>. \u042d\u0442\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b WebFlux \u0434\u043b\u044f Spring MVC <code>RequestEntity<\/code>\u00a0\u0438 <code>ResponseEntity<\/code>, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e.\u00a0\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c router\u00a0DSL Kotlin \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432:<\/p>\n<pre><code class=\"kotlin\">@Configuration class CatRouterConfiguration(     private val catHandler: CatHandler ) {     @Bean     fun apiRouter() = coRouter {         \"\/api\/cats\".nest {             accept(APPLICATION_JSON).nest {                 GET(\"\", catHandler::getAll)                  contentType(APPLICATION_JSON).nest {                     POST(\"\", catHandler::add)                 }                  \"\/{id}\".nest {                     GET(\"\", catHandler::getById)                     DELETE(\"\", catHandler::delete)                      contentType(APPLICATION_JSON).nest {                         PUT(\"\", catHandler::update)                     }                 }             }         }     } }<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>coRouter<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 RouterFunction \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0438\u0445 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432.\u00a0\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u00a0<code>String.nest<\/code> \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432 \u0441 \u043e\u0431\u0449\u0438\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043f\u0443\u0442\u0438.\u00a0\u041f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u043d\u044b \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432 accept \u0438 contentType, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u0435\u0434\u0438\u043a\u0430\u0442\u043e\u0432. \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0444\u0443\u043d\u043a\u0446\u0438\u0438,\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 HTTP \u043c\u0435\u0442\u043e\u0434\u0430\u043c:\u00a0<code>GET<\/code>,\u00a0<code>POST<\/code>,\u00a0<code>PUT<\/code>,\u00a0<code>DELETE<\/code> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435.\u00a0\u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f Handler.<\/p>\n<h3>Handler<\/h3>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 <code>HandlerFunction<\/code> \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u043e\u0442\u0432\u0435\u0442\u044b \u043d\u0430 \u043d\u0438\u0445.\u00a0\u041f\u043e\u0434\u043e\u0431\u043d\u043e \u043c\u0435\u0442\u043e\u0434\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0432 \u043a\u043b\u0430\u0441\u0441 Handler \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u043e\u0433\u043e \u0434\u043b\u044f Kotlin DSL.\u00a0\u042d\u0442\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0447\u0438\u0442\u0430\u044e\u0442 \u0438 \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u044e\u0442 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u0438 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043e\u0431\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c \u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442\u00a0<code>ServerResponse<\/code> \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432 router<\/p>\n<pre><code class=\"kotlin\">suspend fun getById(req: ServerRequest): ServerResponse {     val id = Integer.parseInt(req.pathVariable(\"id\"))     val existingCat = catRepository.findById(id.toLong())      return existingCat?.let {         ServerResponse             .ok()             .contentType(MediaType.APPLICATION_JSON)             .bodyValueAndAwait(it)     } ?: ServerResponse.notFound().buildAndAwait() }  suspend fun add(req: ServerRequest): ServerResponse {     val receivedCat = req.awaitBodyOrNull(CatDto::class)      return receivedCat?.let {         ServerResponse             .ok()             .contentType(MediaType.APPLICATION_JSON)             .bodyValueAndAwait(                 catRepository                     .save(it.toEntity())                     .toDto()             )     } ?: ServerResponse.badRequest().buildAndAwait() }  suspend fun update(req: ServerRequest): ServerResponse {     val id = req.pathVariable(\"id\")      val receivedCat = req.awaitBodyOrNull(CatDto::class)          ?: return ServerResponse.badRequest().buildAndAwait()      val existingCat = catRepository.findById(id.toLong())          ?: return ServerResponse.notFound().buildAndAwait()      return ServerResponse         .ok()         .contentType(MediaType.APPLICATION_JSON)         .bodyValueAndAwait(             catRepository.save(                 receivedCat.toEntity().copy(id = existingCat.id)             ).toDto()         ) }  suspend fun delete(req: ServerRequest): ServerResponse {     val id = req.pathVariable(\"id\")      return if (catRepository.existsById(id.toLong())) {         catRepository.deleteById(id.toLong())         ServerResponse.noContent().buildAndAwait()     } else {         ServerResponse.notFound().buildAndAwait()     } } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u0432\u0438\u0434\u0438\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f Kotlin, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 Spring \u0432\u0441\u0442\u0440\u043e\u0438\u043b \u0432 WebFlux.\u00a0\u041f\u043e \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u044e,\u00a0builder \u043c\u0435\u0442\u043e\u0434\u044b <code>ServerResponse<\/code>, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 Reactor, \u0438\u043c\u0435\u044e\u0442 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u00abawait\u00bb \u0438\u043b\u0438 \u0441\u0443\u0444\u0444\u0438\u043a\u0441 \u00abAndAwait\u00bb \u0434\u043b\u044f \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438, \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u043e\u0440\u0443\u0442\u0438\u043d.\u00a0\u0427\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0447\u0442\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u043e \u0442\u043e\u0442 \u0436\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <code>bodyValueAndAwait<\/code>:<\/p>\n<pre><code class=\"kotlin\">suspend fun ServerResponse.BodyBuilder.bodyValueAndAwait(body: Any): ServerResponse =         bodyValue(body).awaitSingle()<\/code><\/pre>\n<p>\u041a\u0430\u043a \u043c\u044b \u0432\u0438\u0434\u0438\u043c, \u043c\u0435\u0442\u043e\u0434 Reactor\u00a0<code>bodyValue<\/code> \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0435 <code>awaitSingle<\/code> \u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430 <code>kotlinx.coroutines<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043e\u0442 Publisher\u00a0\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b\u00a0\u0442\u0438\u043f\u0430 <code>bodyValueAndAwait<\/code>.<\/p>\n<h3>Repository<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f &#171;\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430&#187; \u043f\u0435\u0440\u0435\u0434 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445 &#8212; \u044d\u0442\u043e Repository (\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439) \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043f\u0435\u0440\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u0438.\u00a0\u041a\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u044f\u043c\u0438, \u0437\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u043c\u0438.\u00a0\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0439 JDBC \u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0443, \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u0443\u044e\u00a0<a href=\"https:\/\/r2dbc.io\/\" rel=\"noopener noreferrer nofollow\">R2DBC<\/a>.\u00a0<\/p>\n<p>Spring Data Reactive, \u043a \u0441\u0447\u0430\u0441\u0442\u044c\u044e, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b \u0434\u043b\u044f \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 \u0438\u0445 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u0430\u043d\u0430\u043b\u043e\u0433\u0438 <code>JpaRepository<\/code> \u0438\u043b\u0438 <code>CrudRepository<\/code>. <\/p>\n<p>\u0415\u0441\u043b\u0438 \u043c\u044b \u0440\u0435\u0448\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0438\u043f R2DBC, \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u044b\u0439 <code>ReactiveCrudRepository,<\/code> \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0431\u0443\u0434\u0443\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0442\u0438\u043f\u044b \u0434\u0430\u043d\u043d\u044b\u0445 Reactor\u00a0<code>Mono<\/code> \u0438\u00a0<code>Flux<\/code>.\u00a0\u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e \u0434\u043b\u044f \u043d\u0430\u0441, \u043a\u0430\u043a \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u044f\u043c\u0438, WebFlux \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f Kotlin \u0438 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b\u00a0\u0442\u0438\u043f\u0430\u00a0<code>CoroutineCrudRepository<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438:<\/p>\n<pre><code class=\"kotlin\">interface CatRepository : CoroutineCrudRepository&lt;Cat, Long> {     override fun findAll(): Flow&lt;Cat>     override suspend fun findById(id: Long): Cat?     override suspend fun existsById(id: Long): Boolean     override suspend fun &lt;S : Cat> save(entity: S): Cat     override suspend fun deleteById(id: Long) }<\/code><\/pre>\n<h3>\u0422\u0435\u0441\u0442\u044b<\/h3>\n<p>\u041a\u0430\u043a \u0445\u043e\u0440\u043e\u0448\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0438 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e\u0439 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u0438\u0438, \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0442\u0449\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.\u00a0\u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u044b \u0434\u0432\u0430 \u043d\u0430\u0431\u043e\u0440\u0430 \u0442\u0435\u0441\u0442\u043e\u0432, \u043e\u0434\u0438\u043d \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u0442, \u0447\u0442\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 H2 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438.\u00a0\u041e\u0431\u0430 \u043e\u043d\u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0442\u0435\u0441\u0442 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e HTTP \u0441\u0442\u0430\u0442\u0443\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442.<\/p>\n<p>\u041e\u0431\u0430 \u0442\u0435\u0441\u0442\u0430 \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u0434\u0432\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439\u00a0<code>aCat()<\/code> \u0438 <code>anotherCat()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u0437\u0434\u0430\u044e\u0442 \u043d\u043e\u0432\u044b\u0439\u00a0<code>Cat<\/code> \u0441 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438 \u0434\u0430\u044e\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f.\u00a0\u0422\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432 \u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0434\u0435\u0442\u0430\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>Cat<\/code>, \u043a\u0440\u043e\u043c\u0435 \u0442\u0435\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043c\u0435\u044e\u0442 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u043a \u0442\u0435\u0441\u0442\u0443, \u0438 \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u044d\u0442\u0438\u0445 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u043e\u043b\u0435\u0439.<\/p>\n<h4>1. \u0422\u0435\u0441\u0442\u044b \u0441 mocking<\/h4>\n<p>\u042d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 \u044f \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0432\u0438\u0436\u0443 \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044f\u0445: \u043c\u044b \u0438\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u043c <code>CatRepository<\/code>, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c Router \u0438 Handler \u0431\u0435\u0437 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e Spring WebFlux \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439\u00a0<code>@WebFluxTest<\/code> \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <code>@Import<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0435 \u0432 \u043a\u043b\u0430\u0441\u0441\u044b Router \u0438 Handler.\u00a0\u041c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>@SpringBootTest<\/code> \u0438\u00a0<code>@AutoConfigureWebTestClient<\/code> \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0438\u0436\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e \u0436\u0435 \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<code>@Import<\/code>.\u00a0\u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u0448\u0438 \u0442\u0435\u0441\u0442\u044b \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0442 \u0431\u044b\u0441\u0442\u0440\u0435\u0435, \u0438 \u043c\u043d\u0435 \u0442\u0430\u043a\u0436\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0435\u0441\u043b\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u044b\u0439 Router \u0438 Handler \u044f\u0432\u043d\u043e \u0443\u043f\u043e\u043c\u0438\u043d\u0430\u044e\u0442\u0441\u044f \u0432\u0432\u0435\u0440\u0445\u0443.<\/p>\n<p>\u041c\u044b\u00a0<a href=\"https:\/\/mockk.io\/\" rel=\"noopener noreferrer nofollow\">\u0438\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u043c<\/a>\u00a0\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f\u00a0Mockk, \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f\u00a0\u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f Kotlin.\u00a0\u041e\u043d \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 Mockk \u0438\u043c\u0435\u0435\u0442 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u043a\u043e\u0440\u0443\u0442\u0438\u043d.\u00a0\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 Mockk \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0437\u0430 \u0440\u0430\u043c\u043a\u0438 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438.\u00a0\u0427\u0442\u043e \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0437\u043d\u0430\u0442\u044c, \u0442\u0430\u043a \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044e\u00a0<code>coEvery<\/code> \u0438\u00a0<code>coAnswers<\/code> \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u0432 <code>CatRepository<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435\u00a0\u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0438\u043c\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c.\u00a0\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, Spring (\u043f\u043e\u043a\u0430) \u043d\u0435 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 beans \u0441 Mockk, \u043a\u0430\u043a \u0431\u0443\u0434\u0442\u043e \u043e\u043d \u0434\u0430\u0435\u0442 \u043d\u0430\u043c\u00a0\u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e <code>@MockBean<\/code> \u0434\u043b\u044f Mockito.\u00a0\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u00a0<a href=\"https:\/\/github.com\/Ninja-Squad\/springmockk\" rel=\"noopener noreferrer nofollow\">SpringMockk<\/a>\u00a0\u0438 \u0435\u0433\u043e \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044e\u00a0<code>@MockkBean<\/code> \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0438\u0436\u0435\u043d\u0438\u044f \u0442\u043e\u0433\u043e \u0436\u0435.<\/p>\n<pre><code class=\"lua\">@WebFluxTest @Import(CatRouterConfiguration::class, CatHandler::class) class MockedRepositoryIntegrationTest(     @Autowired private val client: WebTestClient ) {     @MockkBean     private lateinit var repository: CatRepository      private fun aCat(         name: String = \"Obi\",         type: String = \"Dutch Ringtail\",         age: Int = 3     ) =         Cat(             name = name,             type = type,             age = age         )      private fun anotherCat(         name: String = \"Wan\",         type: String = \"Japanese Bobtail\",         age: Int = 5     ) =         aCat(             name = name,             type = type,             age = age         )      @Test     fun `Retrieve all cats`() {         every {             repository.findAll()         } returns flow {             emit(aCat())             emit(anotherCat())         }          client             .get()             .uri(\"\/api\/cats\")             .exchange()             .expectStatus()             .isOk             .expectBodyList&lt;CatDto>()             .hasSize(2)             .contains(aCat().toDto(), anotherCat().toDto())     }      @Test     fun `Retrieve cat by existing id`() {         coEvery {             repository.findById(any())         } coAnswers {             aCat()         }          client             .get()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isOk             .expectBody&lt;CatDto>()             .isEqualTo(aCat().toDto())     }      @Test     fun `Retrieve cat by non-existing id`() {         coEvery {             repository.findById(any())         } returns null          client             .get()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isNotFound     }      @Test     fun `Add a new cat`() {         val savedCat = slot&lt;Cat>()         coEvery {             repository.save(capture(savedCat))         } coAnswers {             savedCat.captured         }          client             .post()             .uri(\"\/api\/cats\/\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .bodyValue(aCat().toDto())             .exchange()             .expectStatus()             .isOk             .expectBody&lt;CatDto>()             .isEqualTo(savedCat.captured.toDto())     }      @Test     fun `Add a new cat with empty request body`() {         val savedCat = slot&lt;Cat>()         coEvery {             repository.save(capture(savedCat))         } coAnswers {             savedCat.captured         }          client             .post()             .uri(\"\/api\/cats\/\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .body(fromValue(\"{}\"))             .exchange()             .expectStatus()             .isBadRequest     }      @Test     fun `Update a cat`() {         coEvery {             repository.findById(any())         } coAnswers {             aCat()         }          val savedCat = slot&lt;Cat>()         coEvery {             repository.save(capture(savedCat))         } coAnswers {             savedCat.captured         }          val updatedCat = aCat(name = \"New fancy name\").toDto()          client             .put()             .uri(\"\/api\/cats\/2\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .bodyValue(updatedCat)             .exchange()             .expectStatus()             .isOk             .expectBody&lt;CatDto>()             .isEqualTo(updatedCat)     }      @Test     fun `Update cat with non-existing id`() {         val requestedId = slot&lt;Long>()         coEvery {             repository.findById(capture(requestedId))         } coAnswers {             nothing         }          val updatedCat = aCat(name = \"New fancy name\").toDto()          client             .put()             .uri(\"\/api\/cats\/2\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .bodyValue(updatedCat)             .exchange()             .expectStatus()             .isNotFound     }      @Test     fun `Update cat with empty request body id`() {         coEvery {             repository.findById(any())         } coAnswers {             aCat()         }          client             .put()             .uri(\"\/api\/cats\/2\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .body(fromValue(\"{}\"))             .exchange()             .expectStatus()             .isBadRequest     }      @Test     fun `Delete cat with existing id`() {         coEvery {             repository.existsById(any())         } coAnswers {             true         }          coEvery {             repository.deleteById(any())         } coAnswers {             nothing         }          client             .delete()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isNoContent          coVerify { repository.deleteById(any()) }     }      @Test     fun `Delete cat by non-existing id`() {         coEvery {             repository.existsById(any())         } coAnswers {             false         }          client             .delete()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isNotFound          coVerify(exactly = 0) { repository.deleteById(any()) }     } }<\/code><\/pre>\n<h4>2. \u0422\u0435\u0441\u0442\u044b \u0431\u0435\u0437 mocking<\/h4>\n<p>\u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0\u041d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u043b\u0443\u0447\u0448\u0435 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0433\u043e, \u043f\u0440\u0430\u0432\u0434\u0430?\u00a0\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a testcontainers, \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438.\u00a0\u041c\u044b \u0432\u044b\u0431\u0435\u0440\u0435\u043c \u0442\u0440\u0435\u0442\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f <code>@DirtiesContext<\/code> \u0434\u043b\u044f \u0432\u043e\u0441\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430.<\/p>\n<p>\u0412\u0430\u0436\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u043d\u0430\u0431\u043e\u0440\u0430 \u0442\u0435\u0441\u0442\u043e\u0432 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>@AutoConfigureWebTestClient<\/code> \u0432\u043c\u0435\u0441\u0442\u043e\u00a0<code>@WebFluxTest<\/code>.\u00a0\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043e\u0442\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u043f\u043e\u043b\u043d\u0443\u044e \u0430\u0432\u0442\u043e\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0438 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c, \u043d\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043d\u0430\u0448\u0438 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438.<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0438\u043c\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u044b \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f, \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0442\u0435\u0441\u0442\u0430.\u00a0<\/p>\n<p>\u042f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b \u043c\u0435\u0442\u043e\u0434 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u00a0<code>CatRepository.seed<\/code> \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f <code>Cat<\/code> \u0432 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u00a0<code>@AfterEach<\/code> \u043c\u0435\u0442\u043e\u0434 \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u043f\u043e\u0441\u043b\u0435 \u0442\u0435\u0441\u0442\u0430.\u00a0\u041e\u0431\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 <code>runBlocking<\/code> \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f coroutine, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u043c\u044b\u0439 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043f\u043e\u0442\u043e\u043a \u0434\u043e \u0435\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f.\u00a0\u0415\u0441\u043b\u0438 \u043c\u044b \u043d\u0435 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u0442\u043e\u043a, JUnit \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0442\u0435\u0441\u0442 \u0434\u043e \u0442\u043e\u0433\u043e, \u043a\u0430\u043a <code>Cat<\/code> \u0431\u0443\u0434\u0443\u0442 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u044b \u0438\u0437 \u043d\u0435\u0435.<\/p>\n<pre><code class=\"kotlin\">@SpringBootTest @AutoConfigureWebTestClient @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) class InMemoryDatabaseIntegrationTest(     @Autowired val client: WebTestClient,     @Autowired val repository: CatRepository ) {     private fun aCat(         name: String = \"Obi\",         type: String = \"Dutch Ringtail\",         age: Int = 3     ) =         Cat(             name = name,             type = type,             age = age         )      private fun anotherCat(         name: String = \"Wan\",         type: String = \"Japanese Bobtail\",         age: Int = 5     ) =         aCat(             name = name,             type = type,             age = age         )      private fun CatRepository.seed(vararg cats: Cat) =         runBlocking {             repository.saveAll(cats.toList()).toList()         }      @AfterEach     fun afterEach() {         runBlocking {             repository.deleteAll()         }     }      @Test     fun `Retrieve all cats`() {         repository.seed(aCat(), anotherCat())          client             .get()             .uri(\"\/api\/cats\")             .exchange()             .expectStatus()             .isOk             .expectBodyList&lt;CatDto>()             .hasSize(2)             .contains(aCat().toDto(), anotherCat().toDto())     }      @Test     fun `Retrieve cat by existing id`() {         repository.seed(aCat(), anotherCat())          client             .get()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isOk             .expectBody&lt;CatDto>()             .isEqualTo(anotherCat().toDto())     }      @Test     fun `Retrieve cat by non-existing id`() {         client             .get()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isNotFound     }      @Test     fun `Add a new cat`() {         client             .post()             .uri(\"\/api\/cats\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .bodyValue(aCat().toDto())             .exchange()             .expectStatus()             .isOk             .expectBody&lt;CatDto>()             .isEqualTo(aCat().toDto())     }      @Test     fun `Add a new cat with empty request body`() {         client             .post()             .uri(\"\/api\/cats\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .body(fromValue(\"{}\"))             .exchange()             .expectStatus()             .isBadRequest     }      @Test     fun `Update a cat`() {         repository.seed(aCat(), anotherCat())          val updatedCat = aCat(name = \"New fancy name\").toDto()         client             .put()             .uri(\"\/api\/cats\/2\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .bodyValue(updatedCat)             .exchange()             .expectStatus()             .isOk             .expectBody&lt;CatDto>()             .isEqualTo(updatedCat)     }      @Test     fun `Update cat with non-existing id`() {         val updatedCat = aCat(name = \"New fancy name\").toDto()          client             .put()             .uri(\"\/api\/cats\/2\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .bodyValue(updatedCat)             .exchange()             .expectStatus()             .isNotFound     }      @Test     fun `Update cat with empty request body id`() {         client             .put()             .uri(\"\/api\/cats\/2\")             .accept(MediaType.APPLICATION_JSON)             .contentType(MediaType.APPLICATION_JSON)             .body(fromValue(\"{}\"))             .exchange()             .expectStatus()             .isBadRequest     }      @Test     fun `Delete cat with existing id`() {         repository.seed(aCat(), anotherCat())          client             .delete()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isNoContent     }      @Test     fun `Delete cat by non-existing id`() {         client             .delete()             .uri(\"\/api\/cats\/2\")             .exchange()             .expectStatus()             .isNotFound     } }<\/code><\/pre>\n<h3>\u0412\u044b\u0432\u043e\u0434<\/h3>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0435\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Spring WebFlux, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f Kotlin.<\/p>\n<p>\u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u0434\u043e\u0432\u0430\u044f \u0431\u0430\u0437, \u043d\u0435 \u0437\u0430\u043c\u0443\u0441\u043e\u0440\u0435\u043d\u043d\u0430\u044f <code>Mono<\/code> \u0438\u00a0<code>Flux<\/code> (\u0445\u043e\u0442\u044f \u043c\u044b \u0432\u0437\u0430\u043c\u0435\u043d \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e\u00a0<code>suspend<\/code>\u00a0\u0438 <code>Flow<\/code>).<\/p>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435 \u043f\u043e\u0434\u0445\u043e\u0434 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430\u0448 \u043a\u043e\u0434 \u0432 \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u0442\u0438\u043b\u0435, \u043a\u0430\u043a \u043c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438.<\/p>\n<p>\u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u0434\u0432\u0430 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f: \u043e\u0434\u0438\u043d \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c mocking, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 &#8212; \u0431\u0435\u0437 \u043d\u0435\u0433\u043e.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/594625\/\"> https:\/\/habr.com\/ru\/articles\/594625\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u0433\u0434\u0430 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0435\u0442 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441, \u00ab\u043e\u0431\u044b\u0447\u043d\u043e\u0435\u00bb \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0438\u0437 \u043f\u0443\u043b\u0430 \u043f\u043e\u0442\u043e\u043a\u043e\u0432.\u00a0\u042d\u0442\u043e\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u043e\u0442\u0432\u0435\u0442 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d \u0432 \u0441\u043e\u043a\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.\u00a0\u041f\u043e\u043f\u0443\u0442\u043d\u043e \u043d\u0430\u043c \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0432\u0435\u0431-\u0441\u043b\u0443\u0436\u0431\u044b \u0438\u043b\u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0438\u043b\u0438 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u0444\u0430\u0439\u043b \u0438\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430, \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u043e\u0442\u043e\u043a \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0436\u0434\u0430\u0442\u044c, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u043e\u0442\u0432\u0435\u0442.\u00a0\u0414\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u043e\u0442\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u0443\u043b \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0432 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u0441\u0447\u0435\u0440\u043f\u0430\u0442\u044c\u0441\u044f, \u0438 \u0442\u043e\u0433\u0434\u0430 \u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u043c \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435.\u00a0\u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043f\u0443\u043b \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0438 \u043c\u043e\u0434\u0435\u043b\u044c \u00ab\u043f\u043e\u0442\u043e\u043a \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u00bb, \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u043c\u0435\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u043f\u043e\u0442\u043e\u043a \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u0435 \u044f\u0434\u0440\u043e \u200b\u200b\u0426\u041f, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u0438, \u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0432\u0432\u043e\u0434\u0430-\u0432\u044b\u0432\u043e\u0434\u0430, \u043e\u043d\u043e \u0440\u0430\u0437\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u044d\u0442\u0443 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430\u0434 \u0447\u0435\u043c-\u0442\u043e \u0435\u0449\u0435 \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 IO \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e.\u00a0\u041c\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u043c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0435\u0435.\u00a0<\/p>\n<p>\u041f\u043e\u0434\u0445\u043e\u0434 \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0433\u0440\u0443\u043f\u043f\u0430 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0439 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u043b\u0430\u0441\u044c \u0432 \u0438\u043d\u0438\u0446\u0438\u0430\u0442\u0438\u0432\u0435 Reactive Streams, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0438 \u0447\u0435\u0442\u044b\u0440\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 JVM.\u00a0\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043e\u043d\u0438 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u043f\u043e\u0448\u043b\u0438 \u0441\u0432\u043e\u0438\u043c \u043f\u0443\u0442\u0435\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u044d\u0442\u0438\u0445 \u0441\u043e\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0439.\u00a0\u041e\u0434\u043d\u0430 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, Project Reactor, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043e\u0439, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 Spring \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u0432\u0435\u0431-\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 Spring WebFlux.\u00a0<\/p>\n<p>\u042d\u0442\u043e\u0442 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u0442\u0435\u043a \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0437\u043d\u0430\u043a\u043e\u043c\u043e\u0439 \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0438 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439, \u0435\u0441\u043b\u0438 \u0432\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441 Spring MVC, \u043d\u043e \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043a\u0440\u0438\u0432\u043e\u0439 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f.\u00a0<\/p>\n<p>\u0412\u0437\u0433\u043b\u044f\u043d\u0438\u0442\u0435 \u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043d\u0438\u0436\u0435.<\/p>\n<pre><code class=\"kotlin\">@RestController @RequestMapping(\"\/cats\") class CatController(     @Autowired val catRepository: CatRepository ) {     @PutMapping(\"{id}\")     fun updateProduct(@PathVariable(value = \"id\") id: Long, @RequestBody newCat: Cat): Mono&lt;ResponseEntity&lt;Cat?>> {         return catRepository             .findById(id)             .flatMap { existingCat ->                 catRepository.save(                     existingCat.copy(                         name = newCat.name,                         type = newCat.type,                         age = newCat.age                     )                 )             }             .map { updatedCat -> ResponseEntity.ok(updatedCat) }             .defaultIfEmpty(ResponseEntity.notFound().build())     } }<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u0442\u0435, \u043f\u043e\u043b\u044e\u0431\u0438\u0432\u0448\u0438\u0439\u0441\u044f \u043d\u0430\u043c \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c \u0437\u0430\u043c\u0435\u043d\u0435\u043d \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u0434\u0438\u0433\u043c\u043e\u0439 \u0441 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e\u043c \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0442\u043e\u0440\u043e\u0432.\u00a0\u041d\u0430\u0448 \u043a\u043e\u0434 \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0445 \u0446\u0435\u043f\u043e\u0447\u0435\u043a \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0442 \u0438\u0437\u0434\u0430\u0442\u0435\u043b\u0435\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430\u043c.\u00a0\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u0435\u0442\u044c \u0434\u0435\u043b\u043e \u0441 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u044f\u043c\u0438, \u0442\u0430\u043a\u0438\u043c\u0438 \u043a\u0430\u043a Mono \u0438 Flux, \u043d\u0430 \u0432\u0441\u0435\u0445 \u0443\u0440\u043e\u0432\u043d\u044f\u0445 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0438, \u043a\u0430\u043a \u0442\u0430\u043a\u043e\u0432\u0430\u044f, \u043d\u0430\u0448\u0430 \u043a\u043e\u0434\u043e\u0432\u0430\u044f \u0431\u0430\u0437\u0430 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0439 \u0441 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c.\u00a0\u0412 \u043a\u043e\u043d\u0446\u0435 \u043a\u043e\u043d\u0446\u043e\u0432, \u043a\u0430\u0436\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432\u0441\u0435, \u0447\u0442\u043e \u043c\u044b \u0437\u043d\u0430\u0435\u043c, \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u0435 \u0432\u043a\u0443\u0441\u043d\u043e\u0441\u0442\u0438.\u00a0\u041a\u0430\u0436\u0435\u0442\u0441\u044f, \u044d\u0442\u043e \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0446\u0435\u043d\u0430, \u043c\u043e\u0436\u0435\u043c \u043b\u0438 \u043c\u044b \u043f\u043e\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u0438\u043d\u0430\u0447\u0435?<\/p>\n<p>\u0414\u0430 \u043c\u044b \u043c\u043e\u0436\u0435\u043c!\u00a0Kotlin \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u044f\u0437\u044b\u043a\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c\u0443\u044e Coroutines (\u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b), \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043a\u043e\u043d\u0446\u0435\u043f\u0442\u0443\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442 \u0441\u043e\u0431\u043e\u0439 \u043b\u0435\u0433\u043a\u0438\u0435 \u043f\u043e\u0442\u043e\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0430, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043a\u043e\u0434 \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a, \u043a\u0430\u043a \u043c\u044b \u043f\u0440\u0438\u0432\u044b\u043a\u043b\u0438 \u043a \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u043c\u0443 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044e.\u00a0\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043d\u0435 \u043f\u043e\u0434\u0432\u0435\u0440\u0433\u0430\u044f \u043d\u0430\u0448\u0443 \u043a\u043e\u0434\u043e\u0432\u0443\u044e \u0431\u0430\u0437\u0443 \u043f\u043e\u043b\u043d\u043e\u0439 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0440\u0430\u0431\u043e\u0442\u043a\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0430\u0448\u0438 \u0434\u0440\u0443\u0437\u044c\u044f \u0438\u0437 Spring \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043b\u0438 \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0440\u043e\u0448\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u043f\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 Kotlin, \u0438 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043a\u043e\u0440\u0443\u0442\u0438\u043d, \u0432 \u0441\u0432\u043e\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443.\u00a0\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u044b\u00a0\u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043e\u0431\u0449\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0433\u043e API, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u043c\u044b \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Reactor \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c.<\/p>\n<p>\u0417\u0430\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043e\u0432\u0430\u043b\u0438\u0441\u044c?\u00a0\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0432\u0435\u0431-API \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Spring WebFlux \u0438 \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u0435\u043c\u0441\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f Kotlin \u0432 Spring WebFlux.\u00a0\u042d\u0442\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u00a0<code>coRouter<\/code> (\u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0430, &#8216;co&#8217; \u0434\u043b\u044f \u043a\u043e\u0440\u0443\u0442\u0438\u043d) DSL \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0432 \u043d\u0430\u0448 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 <code>Flow<\/code>, \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0430\u0434\u0430\u043f\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b WebClient \u0441 &#8216;await&#8217;.<br \/>\u0412\u043c\u0435\u0441\u0442\u043e \u043c\u043e\u0434\u0435\u043b\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 <code>@RestController<\/code>, <code>@RequestMapping<\/code> \u0438 <code>@Service<\/code> \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0443\u044e \u0432\u0435\u0431-\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0443\u044e \u043d\u043e\u0432\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0446\u0438\u0438 (Router) \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (Handler).<\/p>\n<p>\u041c\u044b \u0441\u043b\u0435\u0434\u0443\u0435\u043c \u0437\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c \u0438\u0437\u0432\u043d\u0435.\u00a0\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442\u0441\u044f \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043e\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430\u00a0<code>@RequestMapping<\/code>.\u00a0\u0417\u0430\u0442\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u043c\u0443 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0443 \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u043c.\u00a0\u041d\u0430\u043a\u043e\u043d\u0435\u0446, \u043c\u044b \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043c \u043a \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u043e\u043c\u0443 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044e.\u00a0\u042d\u0442\u043e\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0441\u043b\u043e\u0439 \u0438\u043c\u0435\u0435\u0442 \u0442\u043e \u0436\u0435 \u0438\u043c\u044f, \u0447\u0442\u043e \u0438 \u0432 Spring MVC, \u043d\u043e \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f, \u043b\u0435\u0436\u0430\u0449\u0430\u044f \u0432 \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u0435, \u0441\u043e\u0432\u0441\u0435\u043c \u0434\u0440\u0443\u0433\u0430\u044f, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u043d\u0435\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0449\u0438\u043c.\u00a0\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0438\u0445 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u043e\u0432 \u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432, \u0442\u043e\u0433\u0434\u0430 \u044d\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f WebFlux, \u0438 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0441\u0442\u0430\u0442\u0435\u0439 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u044d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434.\u00a0\u041e\u0434\u043d\u0430\u043a\u043e \u044f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0438\u0434\u0438\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0434\u043b\u044f Kotlin.\u00a0\u0412\u0441\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043a\u043e\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432\u00a0<a href=\"https:\/\/github.com\/BjornvdLaan\/spring-boot-webflux-kotlin-h2-example\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 GitHub<\/a>.<\/p>\n<h3>\u041d\u0430\u0448 \u043f\u0440\u0438\u043c\u0435\u0440<\/h3>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c API \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 CMS (Cat Management System), \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u0447\u0442\u0435\u043d\u0438\u044f, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f (CRUD).\u00a0\u041d\u0438\u0436\u0435 \u0432\u044b \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d \u043e\u0431\u0437\u043e\u0440 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c, \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u043e\u0442\u0432\u0435\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th data-colwidth=\"124\" width=\"124\">\n<p><strong>HTTP <\/strong>\u043c\u0435\u0442\u043e\u0434<\/p>\n<\/th>\n<th data-colwidth=\"111\" width=\"111\">\n<p><strong>Route<\/strong><\/p>\n<\/th>\n<th data-colwidth=\"147\" width=\"147\">\n<p><strong>HTTP <\/strong>\u0421\u0442\u0430\u0442\u0443\u0441 \u043e\u0442\u0432\u0435\u0442\u0430 <\/p>\n<\/th>\n<th>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>GET<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>200 Ok<\/p>\n<\/td>\n<td>\n<p>\u0412\u0441\u0435 cat \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u044b<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>GET<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>200 Ok<\/p>\n<\/td>\n<td>\n<p>\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f cat \u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>GET<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>404 Not Found<\/p>\n<\/td>\n<td>\n<p>\u0417\u0430\u043f\u0440\u0430\u0448\u0435\u043d cat \u0441 \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u043e\u043c<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>POST<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>201 Created<\/p>\n<\/td>\n<td>\n<p>\u041d\u043e\u0432\u044b\u0439 cat \u0441\u043e\u0437\u0434\u0430\u043d \u0443\u0441\u043f\u0435\u0448\u043d\u043e<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>POST<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>400 Bad Request<\/p>\n<\/td>\n<td>\n<p>\u041d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 cat<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>PUT<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>200 Ok<\/p>\n<\/td>\n<td>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 cat \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>PUT<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>400 Bad Request<\/p>\n<\/td>\n<td>\n<p>Cat \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>PUT<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>404 Not Found<\/p>\n<\/td>\n<td>\n<p>\u0417\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e cat <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>DELETE<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>204 No Content<\/p>\n<\/td>\n<td>\n<p>\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 cat \u0442\u0435\u043f\u0435\u0440\u044c \u0443\u0434\u0430\u043b\u0435\u043d<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td data-colwidth=\"124\" width=\"124\">\n<p>DELETE<\/p>\n<\/td>\n<td data-colwidth=\"111\" width=\"111\">\n<p>\/api\/cats\/{id}<\/p>\n<\/td>\n<td data-colwidth=\"147\" width=\"147\">\n<p>404 Not Found<\/p>\n<\/td>\n<td>\n<p>\u0417\u0430\u043f\u0440\u043e\u0448\u0435\u043d\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0433\u043e cat <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430 Spring Boot \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<a href=\"https:\/\/start.spring.io\/\" rel=\"noopener noreferrer nofollow\">Spring Initializr<\/a>.\u00a0\u041c\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u043f\u0440\u043e\u0435\u043a\u0442 Gradle, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0439 Kotlin \u0438 \u0443\u043f\u0430\u043a\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0432 \u0432\u0438\u0434\u0435 jar-\u0444\u0430\u0439\u043b\u0430.\u00a0\u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438: Spring Reactive Web, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u0442 WebFlux \u0438 Netty, Spring Data R2DBC \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u0438 H2 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0417\u0430\u0442\u0435\u043c \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0449\u0435 \u0434\u0432\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043b\u044f Mockk \u0438 SpringMockk:<\/p>\n<pre><code class=\"cmake\">dependencies {     ...     testImplementation(\"io.mockk:mockk:1.10.2\")     testImplementation(\"com.ninja-squad:springmockk:3.0.1\")     ... }<\/code><\/pre>\n<p>\u041e\u043d\u0438 \u043d\u0435 \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u0442\u0440\u043e\u0433\u043e \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445.\u00a0\u042f \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0432\u0430\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c Maven Central \u043d\u0430 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u0439.<\/p>\n<h3>Router<\/h3>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u0438 Router (\u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0438\u0437\u0430\u0442\u043e\u0440) \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0442\u0438\u043f\u0430\u00a0<code>ServerRequest<\/code> \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 <code>ServerResponse<\/code>. \u042d\u0442\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b WebFlux \u0434\u043b\u044f Spring MVC <code>RequestEntity<\/code>\u00a0\u0438 <code>ResponseEntity<\/code>, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e.\u00a0\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c router\u00a0DSL Kotlin \u0434\u043b\u044f \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043d\u0430\u0448\u0438\u0445 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432:<\/p>\n<pre><code class=\"kotlin\">@Configuration class CatRouterConfiguration(     private val catHandler: CatHandler ) {     @Bean     fun apiRouter() = coRouter {         \"\/api\/cats\".nest {             accept(APPLICATION_JSON).nest {                 GET(\"\", catHandler::getAll)                  contentType(APPLICATION_JSON).nest {                     POST(\"\", catHandler::add)                 }                  \"\/{id}\".nest {                     GET(\"\", catHandler::getById)                     DELETE(\"\", catHandler::delete)                      contentType(APPLICATION_JSON).nest {                         PUT(\"\", catHandler::update)                     }                 }             }         }     } }<\/code><\/pre>\n<p>\u0424\u0443\u043d\u043a\u0446\u0438\u044f <code>coRouter<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 RouterFunction \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0438\u0445 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432.\u00a0\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u00a0<code>String.nest<\/code> \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432 \u0441 \u043e\u0431\u0449\u0438\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043f\u0443\u0442\u0438.\u00a0\u041f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u043d\u044b \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432 accept \u0438 contentType, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0434\u0440\u0443\u0433\u0438\u0445 \u043f\u0440\u0435\u0434\u0438\u043a\u0430\u0442\u043e\u0432. \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 \u0444\u0443\u043d\u043a\u0446\u0438\u0438,\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 HTTP \u043c\u0435\u0442\u043e\u0434\u0430\u043c:\u00a0<code>GET<\/code>,\u00a0<code>POST<\/code>,\u00a0<code>PUT<\/code>,\u00a0<code>DELETE<\/code> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435.\u00a0\u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f Handler.<\/p>\n<h3>Handler<\/h3>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 <code>HandlerFunction<\/code> \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e\u0442 \u043e\u0442\u0432\u0435\u0442\u044b \u043d\u0430 \u043d\u0438\u0445.\u00a0\u041f\u043e\u0434\u043e\u0431\u043d\u043e \u043c\u0435\u0442\u043e\u0434\u0430\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u0441\u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0432 \u043a\u043b\u0430\u0441\u0441 Handler \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u043e\u0433\u043e \u0434\u043b\u044f Kotlin DSL.\u00a0\u042d\u0442\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0447\u0438\u0442\u0430\u044e\u0442 \u0438 \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u044e\u0442 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u0438 \u0442\u0435\u043b\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043e\u0431\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u043a \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c \u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442\u00a0<code>ServerResponse<\/code> \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432 router<\/p>\n<pre><code class=\"kotlin\">suspend fun getById(req: ServerRequest): ServerResponse {     val id = Integer.parseInt(req.pathVariable(\"id\"))     val existingCat = catRepository.findById(id.toLong())      return existingCat?.let {         ServerResponse             .ok()             .contentType(MediaType.APPLICATION_JSON)             .bodyValueAndAwait(it)     } ?: ServerResponse.notFound().buildAndAwait() }  suspend fun add(req: ServerRequest): ServerResponse {     val receivedCat = req.awaitBodyOrNull(CatDto::class)      return receivedCat?.let {         ServerResponse             .ok()             .contentType(MediaType.APPLICATION_JSON)             .bodyValueAndAwait(                 catRepository                     .save(it.toEntity())                     .toDto()             )     } ?: ServerResponse.badRequest().buildAndAwait() }  suspend fun update(req: ServerRequest): ServerResponse {     val id = req.pathVariable(\"id\")      val receivedCat = req.awaitBodyOrNull(CatDto::class)          ?: return ServerResponse.badRequest().buildAndAwait()      val existingCat = catRepository.findById(id.toLong())          ?: return ServerResponse.notFound().buildAndAwait()      return ServerResponse         .ok()         .contentType(MediaType.APPLICATION_JSON)         .bodyValueAndAwait(             catRepository.save(                 receivedCat.toEntity().copy(id = existingCat.id)             ).toDto()         ) }  suspend fun<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-400317","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400317","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=400317"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400317\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=400317"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=400317"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=400317"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}