diff --git a/gradle/docs.gradle b/gradle/docs.gradle index af005bef80..67bd1c316b 100644 --- a/gradle/docs.gradle +++ b/gradle/docs.gradle @@ -77,6 +77,9 @@ dokka { externalDocumentationLink { url = new URL("https://www.reactive-streams.org/reactive-streams-1.0.1-javadoc/") } + externalDocumentationLink { + url = new URL("https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/") + } } configurations { diff --git a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt index f230f79a8d..8e5f59bb52 100644 --- a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt +++ b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/CoRouterFunctionDsl.kt @@ -27,16 +27,38 @@ import org.springframework.web.reactive.function.server.RouterFunctions.nest import java.net.URI /** - * Coroutines variant of [router]. + * Allow to create easily a WebFlux.fn [RouterFunction] with a [Coroutines router Kotlin DSL][CoRouterFunctionDsl]. + * + * Example: + * + * ``` + * @Configuration + * class RouterConfiguration { + * + * @Bean + * fun mainRouter(userHandler: UserHandler) = coRouter { + * accept(TEXT_HTML).nest { + * (GET("/user/") or GET("/users/")).invoke(userHandler::findAllView) + * GET("/users/{login}", userHandler::findViewById) + * } + * accept(APPLICATION_JSON).nest { + * (GET("/api/user/") or GET("/api/users/")).invoke(userHandler::findAll) + * POST("/api/users/", userHandler::create) + * } + * } + * + * } + * ``` * * @author Sebastien Deleuze + * @see router * @since 5.2 */ fun coRouter(routes: (CoRouterFunctionDsl.() -> Unit)) = CoRouterFunctionDsl(routes).build() /** - * Coroutines variant of [RouterFunctionDsl]. + * Provide a WebFlux.fn [RouterFunction] Coroutines Kotlin DSL created by [`coRouter { }`][coRouter] in order to be able to write idiomatic Kotlin code. * * @author Sebastien Deleuze * @since 5.2 diff --git a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt index e85715c010..f718c097f9 100644 --- a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt +++ b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/RouterFunctionDsl.kt @@ -26,18 +26,16 @@ import java.net.URI import java.util.function.Supplier /** - * Allow to create easily a WebFlux.fn `RouterFunction` from a Kotlin - * router DSL leveraging WebFlux.fn Java API ([RouterFunction], [RequestPredicate], - * [HandlerFunction]). + * Allow to create easily a WebFlux.fn [RouterFunction] with a [Reactive router Kotlin DSL][RouterFunctionDsl]. * * Example: * * ``` * @Configuration - * class ApplicationRoutes(val userHandler: UserHandler) { + * class RouterConfiguration { * * @Bean - * fun mainRouter() = router { + * fun mainRouter(userHandler: UserHandler) = router { * accept(TEXT_HTML).nest { * (GET("/user/") or GET("/users/")).invoke(userHandler::findAllView) * GET("/users/{login}", userHandler::findViewById) @@ -51,14 +49,13 @@ import java.util.function.Supplier * } * ``` * @author Sebastien Deleuze - * @see RouterFunctionDsl - * @see RouterFunctions.Builder + * @see coRouter * @since 5.0 */ fun router(routes: RouterFunctionDsl.() -> Unit) = RouterFunctionDsl(routes).build() /** - * Provide a [RouterFunction] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * Provide a WebFlux.fn [RouterFunction] Reactive Kotlin DSL created by [`router { }`][router] in order to be able to write idiomatic Kotlin code. * * @author Sebastien Deleuze * @author Yevhenii Melnyk diff --git a/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt b/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt index e314fc4810..bb6708b49d 100644 --- a/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt +++ b/spring-webmvc/src/main/kotlin/org/springframework/web/servlet/function/RouterFunctionDsl.kt @@ -25,18 +25,16 @@ import java.util.* import java.util.function.Supplier /** - * Allow to create easily a WebMvc.fn `RouterFunction` from a Kotlin router - * DSL leveraging WebMvc.fn the Java API ([RouterFunction], [RequestPredicate], - * [HandlerFunction]). + * Allow to create easily a WebMvc.fn [RouterFunction] with a [Reactive router Kotlin DSL][RouterFunctionDsl]. * * Example: * * ``` * @Configuration - * class ApplicationRoutes(val userHandler: UserHandler) { + * class RouterConfiguration { * * @Bean - * fun mainRouter() = router { + * fun mainRouter(userHandler: UserHandler) = router { * accept(TEXT_HTML).nest { * (GET("/user/") or GET("/users/")).invoke(userHandler::findAllView) * GET("/users/{login}", userHandler::findViewById) @@ -50,14 +48,12 @@ import java.util.function.Supplier * } * ``` * @author Sebastien Deleuze - * @see RouterFunctionDsl - * @see RouterFunctions.Builder * @since 5.2 */ fun router(routes: (RouterFunctionDsl.() -> Unit)) = RouterFunctionDsl(routes).build() /** - * Provide a WebMvc.fn [RouterFunction] Kotlin DSL in order to be able to write idiomatic Kotlin code. + * Provide a WebMvc.fn [RouterFunction] Reactive Kotlin DSL created by [`router { }`][router] in order to be able to write idiomatic Kotlin code. * * @author Sebastien Deleuze * @since 5.2 diff --git a/src/docs/asciidoc/languages/kotlin.adoc b/src/docs/asciidoc/languages/kotlin.adoc index f9c77fb9c4..2b50b1a01c 100644 --- a/src/docs/asciidoc/languages/kotlin.adoc +++ b/src/docs/asciidoc/languages/kotlin.adoc @@ -229,10 +229,11 @@ which lets you deal with profiles and `Environment` for customizing how beans are registered. In the following example notice that: - - Type inference usually allows to avoid specifying the type for bean references like `ref("bazBean")` - - It is possible to use Kotlin top level functions to declare beans using callable references like `bean(::myRouter)` in this example - - When specifying `bean()` or `bean(::myRouter)`, parameters are autowired by type - - The `FooBar` bean will be registered only if the `foobar` profile is active + +* Type inference usually allows to avoid specifying the type for bean references like `ref("bazBean")` +* It is possible to use Kotlin top level functions to declare beans using callable references like `bean(::myRouter)` in this example +* When specifying `bean()` or `bean(::myRouter)`, parameters are autowired by type +* The `FooBar` bean will be registered only if the `foobar` profile is active [source,kotlin,indent=0] ---- @@ -290,20 +291,23 @@ for more details and up-to-date information. == Web +=== Router DSL -=== WebFlux Router DSL +Spring Framework comes with a Kotlin router DSL available in 3 flavors: -Spring Framework comes with a Kotlin router DSL available in two flavors: +* WebMvc.fn {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.servlet.function/router.html[router { }] +* WebFlux.fn <> {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/router.html[router { }] +* WebFlux.fn <> {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/co-router.html[coRouter { }] - - Reactive with {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/-router-function-dsl/[router { }] - - <> - -These DSL let you use the <> to write clean -and idiomatic Kotlin code to build a `RouterFunction` instance as the following example shows: +These DSL let you write clean and idiomatic Kotlin code to build a `RouterFunction` instance as the following example shows: [source,kotlin,indent=0] ---- - val myRouter: RouterFunction = router { +@Configuration +class RouterRouterConfiguration { + + @Bean + fun mainRouter(userHandler: UserHandler) = router { accept(TEXT_HTML).nest { GET("/") { ok().render("index") } GET("/sse") { ok().render("sse") } @@ -319,46 +323,56 @@ and idiomatic Kotlin code to build a `RouterFunction` instance as the following } resources("/**", ClassPathResource("static/")) } +} ---- NOTE: This DSL is programmatic, meaning that it allows custom registration logic of beans through an `if` expression, a `for` loop, or any other Kotlin constructs. That can be useful when you need to register routes depending on dynamic data (for example, from a database). -See https://github.com/mixitconf/mixit/tree/dafd5ccc92dfab6d9c306fcb60b28921a1ccbf79/src/main/kotlin/mixit/web/routes[MiXiT project routes] -for a concrete example. +See https://github.com/mixitconf/mixit/[MiXiT project] for a concrete example. === Coroutines As of Spring Framework 5.2, https://kotlinlang.org/docs/reference/coroutines-overview.html[Coroutines] support -is provided via extensions for WebFlux client and server functional API. A dedicated -{doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/-co-router-function-dsl/[`coRouter { }`] -router DSL is also available. +is provided via: + +* https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html[Deferred] and https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html[Flow] return value support in Spring WebFlux annotated `@Controller` +* Suspending function support in Spring WebFlux annotated `@Controller` +* Extensions for WebFlux {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.client/index.html[client] and {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/index.html[server] functional API. +* WebFlux.fn {doc-root}/spring-framework/docs/{spring-version}/kdoc-api/spring-framework/org.springframework.web.reactive.function.server/co-router.html[coRouter { }] Coroutines extensions use `await` prefix or `AndAwait` suffix, and most are using similar -names to their reactive counterparts, except `exchange` in `WebClient.RequestHeadersSpec` -which translates to `awaitResponse`. +names to their reactive counterparts. [source,kotlin,indent=0] ---- -fun routes(userHandler: UserHandler): RouterFunction = coRouter { - GET("/", userHandler::listView) - GET("/api/user", userHandler::listApi) -} +@Configuration +class RouterConfiguration { + @Bean + fun mainRouter(userHandler: UserHandler) = coRouter { + GET("/", userHandler::listView) + GET("/api/user", userHandler::listApi) + } +} +---- + +[source,kotlin,indent=0] +---- class UserHandler(builder: WebClient.Builder) { private val client = builder.baseUrl("...").build() suspend fun listView(request: ServerRequest): ServerResponse = ServerResponse.ok().renderAndAwait("users", mapOf("users" to - client.get().uri("...").awaitResponse().awaitBody())) + client.get().uri("...").awaitExchange().awaitBody())) suspend fun listApi(request: ServerRequest): ServerResponse = ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).bodyAndAwait( - client.get().uri("...").awaitResponse().awaitBody()) + client.get().uri("...").awaitExchange().awaitBody()) } ----