From 30f61e0c0709c66c9171b0be0210555447a8f55e Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 19 Apr 2017 16:40:23 +0200 Subject: [PATCH] Add body(Object) method to ServerResponse.BodyBuilder This method introduces a new body(Object) to ServerResponse, a shortcut to body(BodyInserters.fromObject(Object)). Note that in the implementation of the method, an `instanceof` check is performed to make sure that the passed argument is not a `Publisher`, as users should call `body(Publisher, Class)` for sending a reactive type. This Publisher-check is also done in the `WebClient`, for the same reasons. Issue: SPR-15461 --- .../function/client/DefaultWebClient.java | 10 +++++++--- .../web/reactive/function/client/WebClient.java | 10 ++++------ .../server/DefaultServerResponseBuilder.java | 16 +++++++++++++++- .../reactive/function/server/ServerResponse.java | 16 +++++++++++++--- .../function/client/DefaultWebClientTests.java | 8 ++++++++ .../DefaultServerResponseBuilderTests.java | 13 ++++++++++--- .../server/NestedRouteIntegrationTests.java | 5 ++--- 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java index 4ad94de023..d613988e5a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClient.java @@ -35,6 +35,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.reactive.ClientHttpRequest; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -258,19 +259,22 @@ class DefaultWebClient implements WebClient { } @Override - public RequestHeadersSpec body(BodyInserter inserter) { + public RequestHeadersSpec body(BodyInserter inserter) { this.inserter = inserter; return this; } @Override - public > RequestHeadersSpec body(S publisher, Class elementClass) { + public > RequestHeadersSpec body(P publisher, Class elementClass) { this.inserter = BodyInserters.fromPublisher(publisher, elementClass); return this; } @Override - public RequestHeadersSpec body(T body) { + public RequestHeadersSpec body(Object body) { + Assert.isTrue(!(body instanceof Publisher), "Please specify the element class by " + + "using body(Publisher, Class)"); + this.inserter = BodyInserters.fromObject(body); return this; } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java index d9e486d7eb..e6d94afcfc 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/WebClient.java @@ -445,10 +445,9 @@ public interface WebClient { /** * Set the body of the request to the given {@code BodyInserter}. * @param inserter the {@code BodyInserter} that writes to the request - * @param the type contained in the body * @return this builder */ - RequestHeadersSpec body(BodyInserter inserter); + RequestHeadersSpec body(BodyInserter inserter); /** * Set the body of the request to the given {@code Publisher}. @@ -458,10 +457,10 @@ public interface WebClient { * @param publisher the {@code Publisher} to write to the request * @param elementClass the class of elements contained in the publisher * @param the type of the elements contained in the publisher - * @param the type of the {@code Publisher} + * @param

the type of the {@code Publisher} * @return this builder */ - > RequestHeadersSpec body(S publisher, Class elementClass); + > RequestHeadersSpec body(P publisher, Class elementClass); /** * Set the body of the request to the given {@code Object}. @@ -469,10 +468,9 @@ public interface WebClient { * {@linkplain org.springframework.web.reactive.function.BodyInserters#fromObject * Object body inserter}. * @param body the {@code Object} to write to the request - * @param the type contained in the body * @return this builder */ - RequestHeadersSpec body(T body); + RequestHeadersSpec body(Object body); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java index f2c9d9066e..a2debbc8b5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java @@ -189,7 +189,21 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { } @Override - public Mono body(BodyInserter inserter) { + public Mono body(Object body) { + Assert.notNull(body, "'body' must not be null"); + Assert.isTrue(!(body instanceof Publisher), "Please specify the element class by using " + + "body(Publisher, Class)"); + + return new DefaultEntityResponseBuilder<>(body, + BodyInserters.fromObject(body)) + .headers(this.headers) + .status(this.statusCode) + .build() + .map(entityResponse -> entityResponse); + } + + @Override + public Mono body(BodyInserter inserter) { Assert.notNull(inserter, "'inserter' must not be null"); return Mono.just(new BodyInserterServerResponse<>(this.statusCode, this.headers, inserter, this.hints)); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java index 64cd5b1a5a..3778e7ded9 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java @@ -341,17 +341,27 @@ public interface ServerResponse { * @param elementClass the class of elements contained in the publisher * @param the type of the elements contained in the publisher * @param

the type of the {@code Publisher} - * @return the built request + * @return the built response */ > Mono body(P publisher, Class elementClass); + /** + * Set the body of the response to the given {@code Object} and return it. + * This convenience method combines {@link #body(BodyInserter)} and + * {@link BodyInserters#fromObject(Object)}. + * @param body the body of the response + * @return the built response + * @throws IllegalArgumentException if {@code body} is a {@link Publisher}, for which + * {@link #body(Publisher, Class)} should be used. + */ + Mono body(Object body); + /** * Set the body of the response to the given {@code BodyInserter} and return it. * @param inserter the {@code BodyInserter} that writes to the response - * @param the type contained in the body * @return the built response */ - Mono body(BodyInserter inserter); + Mono body(BodyInserter inserter); /** * Render the template with the given {@code name} using the given {@code modelAttributes}. diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java index 9a65b1db1b..5ec1377ea4 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultWebClientTests.java @@ -116,6 +116,14 @@ public class DefaultWebClientTests { verifyNoMoreInteractions(this.exchangeFunction); } + @Test(expected = IllegalArgumentException.class) + public void bodyObjectPublisher() throws Exception { + Mono mono = Mono.empty(); + WebClient client = builder().build(); + + client.post().uri("http://example.com").body(mono); + } + private WebClient.Builder builder() { return WebClient.builder().baseUrl("/base").exchangeFunction(this.exchangeFunction); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java index fa26f71cf2..1f7fc73fec 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java @@ -35,9 +35,8 @@ import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; import org.springframework.web.server.ServerWebExchange; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; /** * @author Arjen Poutsma @@ -295,4 +294,12 @@ public class DefaultServerResponseBuilderTests { StepVerifier.create(response.getBody()).expectComplete().verify(); } + @Test(expected = IllegalArgumentException.class) + public void bodyObjectPublisher() throws Exception { + Mono mono = Mono.empty(); + + ServerResponse.ok().body(mono); + } + + } \ No newline at end of file diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/NestedRouteIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/NestedRouteIntegrationTests.java index ae5a3d4aff..86a9278ae2 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/NestedRouteIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/NestedRouteIntegrationTests.java @@ -25,7 +25,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import static org.junit.Assert.*; -import static org.springframework.web.reactive.function.BodyInserters.fromObject; import static org.springframework.web.reactive.function.server.RequestPredicates.GET; import static org.springframework.web.reactive.function.server.RequestPredicates.path; import static org.springframework.web.reactive.function.server.RouterFunctions.nest; @@ -82,11 +81,11 @@ public class NestedRouteIntegrationTests extends AbstractRouterFunctionIntegrati private static class NestedHandler { public Mono bar(ServerRequest request) { - return ServerResponse.ok().body(fromObject("bar")); + return ServerResponse.ok().body("bar"); } public Mono baz(ServerRequest request) { - return ServerResponse.ok().body(fromObject("baz")); + return ServerResponse.ok().body("baz"); } public Mono variables(ServerRequest request) {