diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/web/webfluxfnrequest/RequestHandler.kt b/framework-docs/src/main/kotlin/org/springframework/docs/web/webfluxfnrequest/RequestHandler.kt index c2a1fc1dfd2..52dfdd841c6 100644 --- a/framework-docs/src/main/kotlin/org/springframework/docs/web/webfluxfnrequest/RequestHandler.kt +++ b/framework-docs/src/main/kotlin/org/springframework/docs/web/webfluxfnrequest/RequestHandler.kt @@ -17,13 +17,13 @@ package org.springframework.docs.web.webfluxfnrequest import org.springframework.web.reactive.function.server.ServerRequest -import reactor.core.publisher.Mono +import org.springframework.web.reactive.function.server.bindAndAwait class RequestHandler { - fun bind(request: ServerRequest) { + suspend fun bind(request: ServerRequest) { // tag::snippet[] - val pet: Mono = request.bind(Pet::class.java) { dataBinder -> dataBinder.setAllowedFields("name") } + val pet: Pet? = request.bindAndAwait{ dataBinder -> dataBinder.setAllowedFields("name") } // end::snippet[] } diff --git a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt index 97bc1fe36d9..be673186788 100644 --- a/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt +++ b/spring-webflux/src/main/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensions.kt @@ -24,6 +24,7 @@ import org.springframework.core.ParameterizedTypeReference import org.springframework.http.MediaType import org.springframework.http.codec.multipart.Part import org.springframework.util.MultiValueMap +import org.springframework.web.bind.WebDataBinder import org.springframework.web.server.WebSession import reactor.core.publisher.Flux import reactor.core.publisher.Mono @@ -202,3 +203,23 @@ fun ServerRequest.Headers.contentTypeOrNull(): MediaType? = fun ServerRequest.pathVariableOrNull(name: String): String? { return pathVariables()[name] } + +/** + * Extension for [ServerRequest.bind] providing a `bindAndAwait()` Coroutines + * variant leveraging Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 7.0.3 + */ +suspend inline fun ServerRequest.bindAndAwait(): T? = + bind(T::class.java).awaitSingleOrNull() + +/** + * Extension for [ServerRequest.bind] providing a `bindAndAwait { ... }` Coroutines + * variant leveraging Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 7.0.3 + */ +suspend inline fun ServerRequest.bindAndAwait(noinline dataBinderCustomizer: (WebDataBinder) -> Unit): T? = + bind(T::class.java, dataBinderCustomizer).awaitSingleOrNull() diff --git a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt index 560355ebfa5..eeeb3dafd07 100644 --- a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt +++ b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/server/ServerRequestExtensionsTests.kt @@ -229,5 +229,35 @@ class ServerRequestExtensionsTests { verify { request.pathVariables() } } + @Test + suspend fun `bindAndAwait non-null`() { + val foo = Foo() + every { request.bind(Foo::class.java) } returns Mono.just(foo) + assertThat(request.bindAndAwait()).isEqualTo(foo) + verify { request.bind(Foo::class.java) } + } + + @Test + suspend fun `bindAndAwait null`() { + every { request.bind(Foo::class.java) } returns Mono.empty() + assertThat(request.bindAndAwait()).isNull() + verify { request.bind(Foo::class.java) } + } + + @Test + suspend fun `bindAndAwait non-null with customize`() { + val foo = Foo() + every { request.bind(Foo::class.java, any()) } returns Mono.just(foo) + assertThat(request.bindAndAwait { it.setAllowedFields("name") }).isEqualTo(foo) + verify { request.bind(Foo::class.java, any()) } + } + + @Test + suspend fun `bindAndAwait null with customize`() { + every { request.bind(Foo::class.java, any()) } returns Mono.empty() + assertThat(request.bindAndAwait { it.setAllowedFields("name") }).isNull() + verify { request.bind(Foo::class.java, any()) } + } + class Foo }