From ebe3b45743eeee34627b41679f47926e276b284f Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 20 Mar 2026 11:54:22 +0100 Subject: [PATCH 1/3] Consistently log request details in ExchangeFunctions Closes gh-36502 --- .../web/reactive/function/client/ExchangeFunctions.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java index a8926ebe71d..b494d2e1d7a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFunctions.java @@ -108,14 +108,16 @@ public abstract class ExchangeFunctions { String logPrefix = getLogPrefix(clientRequest, httpResponse); logResponse(httpResponse, logPrefix); return new DefaultClientResponse( - httpResponse, this.strategies, logPrefix, httpMethod.name() + " " + url, + httpResponse, this.strategies, logPrefix, + WebClientUtils.getRequestDescription(httpMethod, url), () -> createRequest(clientRequest)); }); } private void logRequest(ClientRequest request) { LogFormatUtils.traceDebug(logger, traceOn -> - request.logPrefix() + "HTTP " + request.method() + " " + request.url() + + request.logPrefix() + "HTTP " + + WebClientUtils.getRequestDescription(request.method(), request.url()) + (traceOn ? ", headers=" + formatHeaders(request.headers()) : "") ); } From 00be6d3afd852b6226ac062fbecfb49393d41816 Mon Sep 17 00:00:00 2001 From: Vishnutheep B Date: Mon, 23 Mar 2026 02:02:29 +0530 Subject: [PATCH 2/3] Add null check when setting API version to preserve default value See gh-36514 Signed-off-by: Vishnutheep B --- .../web/client/support/RestClientAdapter.java | 4 +++- .../support/RestClientAdapterTests.java | 21 +++++++++++++++++++ .../client/support/WebClientAdapter.java | 4 +++- .../client/support/WebClientAdapterTests.java | 20 ++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/client/support/RestClientAdapter.java b/spring-web/src/main/java/org/springframework/web/client/support/RestClientAdapter.java index dcc5925c39b..58b80a9d516 100644 --- a/spring-web/src/main/java/org/springframework/web/client/support/RestClientAdapter.java +++ b/spring-web/src/main/java/org/springframework/web/client/support/RestClientAdapter.java @@ -99,7 +99,9 @@ public final class RestClientAdapter implements HttpExchangeAdapter { RestClient.RequestBodySpec spec = setUri(uriSpec, values); spec.headers(headers -> headers.putAll(values.getHeaders())); setCookieHeader(spec, values); - spec.apiVersion(values.getApiVersion()); + if (values.getApiVersion() != null) { + spec.apiVersion(values.getApiVersion()); + } spec.attributes(attributes -> attributes.putAll(values.getAttributes())); setBody(spec, values); return spec; diff --git a/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java b/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java index 95a8d6b8ec4..7b944debeb4 100644 --- a/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java @@ -200,6 +200,27 @@ class RestClientAdapterTests { assertThat(actualResponse).isEqualTo("Hello Spring 2!"); } + @Test + void greetingWithDefaultApiVersion() throws Exception { + prepareResponse(builder -> + builder.setHeader("Content-Type", "text/plain").body("Hello Spring 2!")); + + RestClient restClient = RestClient.builder() + .baseUrl(anotherServer.url("/").toString()) + .defaultApiVersion("1.0") + .apiVersionInserter(ApiVersionInserter.useHeader("X-Version")) + .build(); + + RestClientAdapter adapter = RestClientAdapter.create(restClient); + Service service = HttpServiceProxyFactory.builderFor(adapter).build().createClient(Service.class); + + String actualResponse = service.getGreeting(); + + RecordedRequest request = anotherServer.takeRequest(); + assertThat(request.getHeaders().get("X-Version")).isEqualTo("1.0"); + assertThat(actualResponse).isEqualTo("Hello Spring 2!"); + } + @Test // see gh-36326 void getBodyWithGenericReturnType() { prepareResponse(r -> r.setHeader("Content-Type", "application/json").body("{\"name\":\"Karl\"}")); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/WebClientAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/WebClientAdapter.java index 97c9410fbd4..82496a5dea8 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/WebClientAdapter.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/WebClientAdapter.java @@ -111,7 +111,9 @@ public final class WebClientAdapter extends AbstractReactorHttpExchangeAdapter { WebClient.RequestBodySpec bodySpec = setUri(uriSpec, values); bodySpec.headers(headers -> headers.putAll(values.getHeaders())); bodySpec.cookies(cookies -> cookies.putAll(values.getCookies())); - bodySpec.apiVersion(values.getApiVersion()); + if (values.getApiVersion() != null) { + bodySpec.apiVersion(values.getApiVersion()); + } bodySpec.attributes(attributes -> attributes.putAll(values.getAttributes())); setBody(bodySpec, values); return bodySpec; diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java index aad0fb9ffeb..7387c81c5b1 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java @@ -48,6 +48,7 @@ import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.client.ApiVersionInserter; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.service.annotation.GetExchange; @@ -129,6 +130,25 @@ class WebClientAdapterTests { assertThat(attributes).containsEntry("myAttribute", "myAttributeValue"); } + @Test + void greetingWithDefaultApiVersion() throws InterruptedException { + prepareResponse(builder -> builder.setHeader("Content-Type", "text/plain").body("Hello Spring 2!")); + + WebClient webClient = WebClient.builder() + .baseUrl(this.server.url("/").toString()) + .defaultApiVersion("1.0") + .apiVersionInserter(ApiVersionInserter.useHeader("X-Version")) + .build(); + + StepVerifier.create(initService(webClient, Service.class).getGreeting()) + .expectNext("Hello Spring 2!") + .expectComplete() + .verify(Duration.ofSeconds(5)); + + RecordedRequest request = this.server.takeRequest(); + assertThat(request.getHeaders().get("X-Version")).isEqualTo("1.0"); + } + @Test // see gh-36326 void getBodyWithGenericReturnType() { prepareResponse(r -> r.setHeader("Content-Type", "application/json").body("{\"name\":\"Karl\"}")); From 2dcd56bbaf1a6333f230009cebda542e5762ed8a Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 24 Mar 2026 15:24:33 +0100 Subject: [PATCH 3/3] Polishing contribution Closes gh-36514 --- .../web/client/support/RestClientAdapterTests.java | 9 ++++----- .../function/client/support/WebClientAdapterTests.java | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java b/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java index 7b944debeb4..f04ec866273 100644 --- a/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/support/RestClientAdapterTests.java @@ -200,7 +200,7 @@ class RestClientAdapterTests { assertThat(actualResponse).isEqualTo("Hello Spring 2!"); } - @Test + @Test // gh-36514 void greetingWithDefaultApiVersion() throws Exception { prepareResponse(builder -> builder.setHeader("Content-Type", "text/plain").body("Hello Spring 2!")); @@ -211,10 +211,9 @@ class RestClientAdapterTests { .apiVersionInserter(ApiVersionInserter.useHeader("X-Version")) .build(); - RestClientAdapter adapter = RestClientAdapter.create(restClient); - Service service = HttpServiceProxyFactory.builderFor(adapter).build().createClient(Service.class); - - String actualResponse = service.getGreeting(); + String actualResponse = + HttpServiceProxyFactory.builderFor(RestClientAdapter.create(restClient)).build() + .createClient(Service.class).getGreeting(); RecordedRequest request = anotherServer.takeRequest(); assertThat(request.getHeaders().get("X-Version")).isEqualTo("1.0"); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java index 7387c81c5b1..9338e8e6422 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java @@ -130,7 +130,7 @@ class WebClientAdapterTests { assertThat(attributes).containsEntry("myAttribute", "myAttributeValue"); } - @Test + @Test // gh-36514 void greetingWithDefaultApiVersion() throws InterruptedException { prepareResponse(builder -> builder.setHeader("Content-Type", "text/plain").body("Hello Spring 2!"));