Add body methods with Object parameter to WebFlux

The commit deprecates syncBody(Object) in favor of body(Object)
which has the same behavior in ServerResponse, WebClient and
WebTestClient. It also adds body(Object, Class) and
body(Object, ParameterizedTypeReference) methods in order to support
any reactive type that can be adapted to a Publisher via
ReactiveAdapterRegistry. Related BodyInserters#fromProducer
methods are provided as well.

Shadowed Kotlin body<T>() extensions are deprecated in favor of
bodyWithType<T>() ones, including dedicated Publisher<T> and
Flow<T> variants. Coroutines extensions are adapted as well, and
body(Object) can now be used with suspending functions.

Closes gh-23212
This commit is contained in:
Sebastien Deleuze
2019-07-07 21:03:41 +02:00
parent 0fbc9bf461
commit 2b4d6ce354
33 changed files with 781 additions and 270 deletions

View File

@@ -61,7 +61,7 @@ public class ApplicationContextSpecTests {
.GET("/sessionClassName", request ->
request.session().flatMap(session -> {
String className = session.getClass().getSimpleName();
return ServerResponse.ok().syncBody(className);
return ServerResponse.ok().body(className);
}))
.build();
}

View File

@@ -63,7 +63,7 @@ public class ErrorTests {
EntityExchangeResult<Void> result = this.client.post()
.uri("/post")
.contentType(MediaType.APPLICATION_JSON)
.syncBody(new Person("Dan"))
.body(new Person("Dan"))
.exchange()
.expectStatus().isBadRequest()
.expectBody().isEmpty();

View File

@@ -82,7 +82,7 @@ public class JsonContentTests {
public void postJsonContent() {
this.client.post().uri("/persons")
.contentType(MediaType.APPLICATION_JSON)
.syncBody("{\"name\":\"John\"}")
.body("{\"name\":\"John\"}")
.exchange()
.expectStatus().isCreated()
.expectBody().isEmpty();

View File

@@ -145,7 +145,7 @@ public class ResponseEntityTests {
@Test
public void postEntity() {
this.client.post()
.syncBody(new Person("John"))
.body(new Person("John"))
.exchange()
.expectStatus().isCreated()
.expectHeader().valueEquals("location", "/persons/John")

View File

@@ -116,7 +116,7 @@ public class XmlContentTests {
this.client.post().uri("/persons")
.contentType(MediaType.APPLICATION_XML)
.syncBody(content)
.body(content)
.exchange()
.expectStatus().isCreated()
.expectHeader().valueEquals(HttpHeaders.LOCATION, "/persons/John")

View File

@@ -45,7 +45,7 @@ public class HttpServerTests {
@Before
public void start() throws Exception {
HttpHandler httpHandler = RouterFunctions.toHttpHandler(
route(GET("/test"), request -> ServerResponse.ok().syncBody("It works!")));
route(GET("/test"), request -> ServerResponse.ok().body("It works!")));
this.server = new ReactorHttpServer();
this.server.setHandler(httpHandler);

View File

@@ -41,7 +41,7 @@ public class RouterFunctionTests {
public void setUp() throws Exception {
RouterFunction<?> route = route(GET("/test"), request ->
ServerResponse.ok().syncBody("It works!"));
ServerResponse.ok().body("It works!"));
this.testClient = WebTestClient.bindToRouterFunction(route).build();
}

View File

@@ -18,10 +18,14 @@ package org.springframework.test.web.reactive.server
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import org.junit.Assert.assertEquals
import org.junit.Test
import org.reactivestreams.Publisher
import org.springframework.core.ParameterizedTypeReference
import org.springframework.web.reactive.function.server.router
import java.util.concurrent.CompletableFuture
/**
* Mock object based tests for [WebTestClient] Kotlin extensions
@@ -30,16 +34,31 @@ import org.springframework.web.reactive.function.server.router
*/
class WebTestClientExtensionsTests {
val requestBodySpec = mockk<WebTestClient.RequestBodySpec>(relaxed = true)
private val requestBodySpec = mockk<WebTestClient.RequestBodySpec>(relaxed = true)
val responseSpec = mockk<WebTestClient.ResponseSpec>(relaxed = true)
private val responseSpec = mockk<WebTestClient.ResponseSpec>(relaxed = true)
@Test
fun `RequestBodySpec#body with Publisher and reified type parameters`() {
fun `RequestBodySpec#bodyWithType with Publisher and reified type parameters`() {
val body = mockk<Publisher<Foo>>()
requestBodySpec.body(body)
verify { requestBodySpec.body(body, Foo::class.java) }
requestBodySpec.bodyWithType(body)
verify { requestBodySpec.body(body, object : ParameterizedTypeReference<Foo>() {}) }
}
@Test
@FlowPreview
fun `RequestBodySpec#bodyWithType with Flow and reified type parameters`() {
val body = mockk<Flow<Foo>>()
requestBodySpec.bodyWithType(body)
verify { requestBodySpec.body(body, object : ParameterizedTypeReference<Foo>() {}) }
}
@Test
fun `RequestBodySpec#bodyWithType with CompletableFuture and reified type parameters`() {
val body = mockk<CompletableFuture<Foo>>()
requestBodySpec.bodyWithType<Foo>(body)
verify { requestBodySpec.body(body, object : ParameterizedTypeReference<Foo>() {}) }
}
@Test
@@ -51,7 +70,7 @@ class WebTestClientExtensionsTests {
@Test
fun `KotlinBodySpec#isEqualTo`() {
WebTestClient
.bindToRouterFunction( router { GET("/") { ok().syncBody("foo") } } )
.bindToRouterFunction( router { GET("/") { ok().body("foo") } } )
.build()
.get().uri("/").exchange().expectBody<String>().isEqualTo("foo")
}
@@ -59,7 +78,7 @@ class WebTestClientExtensionsTests {
@Test
fun `KotlinBodySpec#consumeWith`() {
WebTestClient
.bindToRouterFunction( router { GET("/") { ok().syncBody("foo") } } )
.bindToRouterFunction( router { GET("/") { ok().body("foo") } } )
.build()
.get().uri("/").exchange().expectBody<String>().consumeWith { assertEquals("foo", it.responseBody) }
}
@@ -67,7 +86,7 @@ class WebTestClientExtensionsTests {
@Test
fun `KotlinBodySpec#returnResult`() {
WebTestClient
.bindToRouterFunction( router { GET("/") { ok().syncBody("foo") } } )
.bindToRouterFunction( router { GET("/") { ok().body("foo") } } )
.build()
.get().uri("/").exchange().expectBody<String>().returnResult().apply { assertEquals("foo", responseBody) }
}
@@ -75,13 +94,13 @@ class WebTestClientExtensionsTests {
@Test
fun `ResponseSpec#expectBodyList with reified type parameters`() {
responseSpec.expectBodyList<Foo>()
verify { responseSpec.expectBodyList(Foo::class.java) }
verify { responseSpec.expectBodyList(object : ParameterizedTypeReference<Foo>() {}) }
}
@Test
fun `ResponseSpec#returnResult with reified type parameters`() {
responseSpec.returnResult<Foo>()
verify { responseSpec.returnResult(Foo::class.java) }
verify { responseSpec.returnResult(object : ParameterizedTypeReference<Foo>() {}) }
}
class Foo