diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java index e2084f7a21d..5291a0c7685 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java @@ -35,19 +35,19 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder; abstract class AbstractMockServerSpec> implements WebTestClient.MockServerSpec { - private final ExchangeMutatorWebFilter exchangeMutatorFilter = new ExchangeMutatorWebFilter(); + private final ExchangeMutatingWebFilter exchangeMutatingWebFilter = new ExchangeMutatingWebFilter(); private final List filters = new ArrayList<>(4); AbstractMockServerSpec() { - this.filters.add(this.exchangeMutatorFilter); + this.filters.add(this.exchangeMutatingWebFilter); } @Override public T exchangeMutator(UnaryOperator mutator) { - this.exchangeMutatorFilter.register(mutator); + this.exchangeMutatingWebFilter.registerGlobalMutator(mutator); return self(); } @@ -67,7 +67,7 @@ abstract class AbstractMockServerSpec> public WebTestClient.Builder configureClient() { WebHttpHandlerBuilder builder = initHttpHandlerBuilder(); filtersInReverse().forEach(builder::prependFilter); - return new DefaultWebTestClientBuilder(builder.build(), this.exchangeMutatorFilter); + return new DefaultWebTestClientBuilder(builder.build(), this.exchangeMutatingWebFilter); } /** diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java index 4dfca1dbb54..9c825241169 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java @@ -69,7 +69,7 @@ class DefaultWebTestClient implements WebTestClient { private final WiretapConnector wiretapConnector; - private final ExchangeMutatorWebFilter exchangeMutatorWebFilter; + private final ExchangeMutatingWebFilter exchangeMutatingWebFilter; private final Duration timeout; @@ -77,20 +77,20 @@ class DefaultWebTestClient implements WebTestClient { DefaultWebTestClient(WebClient.Builder webClientBuilder, ClientHttpConnector connector, - ExchangeMutatorWebFilter webFilter, Duration timeout) { + ExchangeMutatingWebFilter exchangeMutatingWebFilter, Duration timeout) { Assert.notNull(webClientBuilder, "WebClient.Builder is required"); this.wiretapConnector = new WiretapConnector(connector); this.webClient = webClientBuilder.clientConnector(this.wiretapConnector).build(); - this.exchangeMutatorWebFilter = webFilter; + this.exchangeMutatingWebFilter = exchangeMutatingWebFilter; this.timeout = (timeout != null ? timeout : Duration.ofSeconds(5)); } private DefaultWebTestClient(DefaultWebTestClient webTestClient, ExchangeFilterFunction filter) { this.webClient = webTestClient.webClient.filter(filter); this.wiretapConnector = webTestClient.wiretapConnector; - this.exchangeMutatorWebFilter = webTestClient.exchangeMutatorWebFilter; + this.exchangeMutatingWebFilter = webTestClient.exchangeMutatingWebFilter; this.timeout = webTestClient.timeout; } @@ -150,13 +150,13 @@ class DefaultWebTestClient implements WebTestClient { @Override public WebTestClient exchangeMutator(UnaryOperator mutator) { - Assert.notNull(this.exchangeMutatorWebFilter, + Assert.notNull(this.exchangeMutatingWebFilter, "This option is applicable only for tests without an actual running server"); return filter((request, next) -> { String requestId = request.headers().getFirst(WiretapConnector.REQUEST_ID_HEADER_NAME); Assert.notNull(requestId, "No request-id header"); - this.exchangeMutatorWebFilter.register(requestId, mutator); + this.exchangeMutatingWebFilter.registerPerRequestMutator(requestId, mutator); return next.exchange(request); }); } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java index e7320d6aa3e..019ebf1ad48 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java @@ -37,7 +37,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder { private final ClientHttpConnector connector; - private final ExchangeMutatorWebFilter exchangeMutatorFilter; + private final ExchangeMutatingWebFilter exchangeMutatingWebFilter; private Duration responseTimeout; @@ -48,12 +48,12 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder { DefaultWebTestClientBuilder(ClientHttpConnector connector) { this.connector = connector; - this.exchangeMutatorFilter = null; + this.exchangeMutatingWebFilter = null; } - DefaultWebTestClientBuilder(HttpHandler httpHandler, ExchangeMutatorWebFilter exchangeMutatorFilter) { + DefaultWebTestClientBuilder(HttpHandler httpHandler, ExchangeMutatingWebFilter exchangeMutatingWebFilter) { this.connector = new HttpHandlerConnector(httpHandler); - this.exchangeMutatorFilter = exchangeMutatorFilter; + this.exchangeMutatingWebFilter = exchangeMutatingWebFilter; } @@ -96,7 +96,7 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder { @Override public WebTestClient build() { return new DefaultWebTestClient(this.webClientBuilder, this.connector, - this.exchangeMutatorFilter, this.responseTimeout); + this.exchangeMutatingWebFilter, this.responseTimeout); } } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatorWebFilter.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatingWebFilter.java similarity index 70% rename from spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatorWebFilter.java rename to spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatingWebFilter.java index 709fa741364..87395d1f6ea 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatorWebFilter.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatingWebFilter.java @@ -29,15 +29,18 @@ import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; /** - * WebFilter that applies global and request-specific transformation on + * WebFilter for applying global and per-request transformations to a * {@link ServerWebExchange}. * * @author Rossen Stoyanchev * @since 5.0 */ -class ExchangeMutatorWebFilter implements WebFilter { +class ExchangeMutatingWebFilter implements WebFilter { - private volatile Function globalMutator; + private static final Function NO_OP_MUTATOR = e -> e; + + + private volatile Function globalMutator = NO_OP_MUTATOR; private final Map> perRequestMutators = new ConcurrentHashMap<>(4); @@ -47,9 +50,9 @@ class ExchangeMutatorWebFilter implements WebFilter { * Register a global transformation function to apply to all requests. * @param mutator the transformation function */ - public void register(UnaryOperator mutator) { + public void registerGlobalMutator(UnaryOperator mutator) { Assert.notNull(mutator, "'mutator' is required"); - this.globalMutator = this.globalMutator != null ? this.globalMutator.andThen(mutator) : mutator; + this.globalMutator = this.globalMutator.andThen(mutator); } /** @@ -57,7 +60,7 @@ class ExchangeMutatorWebFilter implements WebFilter { * @param requestId the "request-id" header value identifying the request * @param mutator the transformation function */ - public void register(String requestId, UnaryOperator mutator) { + public void registerPerRequestMutator(String requestId, UnaryOperator mutator) { this.perRequestMutators.compute(requestId, (s, value) -> value != null ? value.andThen(mutator) : mutator); } @@ -65,18 +68,15 @@ class ExchangeMutatorWebFilter implements WebFilter { @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - - if (this.globalMutator != null) { - exchange = this.globalMutator.apply(exchange); - } - - String requestId = WiretapConnector.getRequestId(exchange.getRequest().getHeaders()); - Function mutator = this.perRequestMutators.remove(requestId); - if (mutator != null) { - exchange = mutator.apply(exchange); - } - + exchange = this.globalMutator.apply(exchange); + exchange = getMutatorFor(exchange).apply(exchange); return chain.filter(exchange); } + private Function getMutatorFor(ServerWebExchange exchange) { + String id = WiretapConnector.getRequestId(exchange.getRequest().getHeaders()); + Function mutator = this.perRequestMutators.remove(id); + return mutator != null ? mutator : NO_OP_MUTATOR; + } + } 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 44ed674564a..b8e1aae32ff 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 @@ -375,22 +375,23 @@ public interface WebClient { * Exchange the request for a {@code ClientResponse} with full access * to the response status and headers before extracting the body. * - *

Use {@link Mono#then(Function)} or {@link Mono#flatMap(Function)} - * to compose further on the response: + *

Use {@link Mono#flatMap(Function)} or + * {@link Mono#flatMapMany(Function)} to compose further on the response: * *

 		 *	Mono<Pojo> mono = client.get().uri("/")
 		 *		.accept(MediaType.APPLICATION_JSON)
 		 *		.exchange()
-		 *		.then(response -> response.bodyToMono(Pojo.class));
+		 *		.flatMap(response -> response.bodyToMono(Pojo.class));
 		 *
 		 *	Flux<Pojo> flux = client.get().uri("/")
 		 *		.accept(MediaType.APPLICATION_STREAM_JSON)
 		 *		.exchange()
-		 *		.then(response -> response.bodyToFlux(Pojo.class));
+		 *		.flatMapMany(response -> response.bodyToFlux(Pojo.class));
 		 * 
* * @return a {@code Mono} with the response + * @see #retrieve() */ Mono exchange();