Add ServerRequest.bindAndAwait Kotlin extension

Closes gh-36092
This commit is contained in:
Sébastien Deleuze
2026-01-02 18:06:27 +01:00
parent 3ddaf51f5d
commit 6939cb90df
3 changed files with 54 additions and 3 deletions
@@ -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<Pet> = request.bind(Pet::class.java) { dataBinder -> dataBinder.setAllowedFields("name") }
val pet: Pet? = request.bindAndAwait<Pet>{ dataBinder -> dataBinder.setAllowedFields("name") }
// end::snippet[]
}
@@ -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<Foo>()` Coroutines
* variant leveraging Kotlin reified type parameters.
*
* @author Sebastien Deleuze
* @since 7.0.3
*/
suspend inline fun <reified T : Any> ServerRequest.bindAndAwait(): T? =
bind(T::class.java).awaitSingleOrNull()
/**
* Extension for [ServerRequest.bind] providing a `bindAndAwait<Foo> { ... }` Coroutines
* variant leveraging Kotlin reified type parameters.
*
* @author Sebastien Deleuze
* @since 7.0.3
*/
suspend inline fun <reified T : Any> ServerRequest.bindAndAwait(noinline dataBinderCustomizer: (WebDataBinder) -> Unit): T? =
bind(T::class.java, dataBinderCustomizer).awaitSingleOrNull()
@@ -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<Foo>()).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<Foo>()).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<Foo> { 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<Foo> { it.setAllowedFields("name") }).isNull()
verify { request.bind(Foo::class.java, any()) }
}
class Foo
}