diff --git a/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc b/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc
index 0828d205fd..f73d22e1a0 100644
--- a/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc
+++ b/framework-docs/modules/ROOT/pages/integration/rest-clients.adoc
@@ -388,7 +388,8 @@ either using `WebClient`:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebClient client = WebClient.builder().baseUrl("https://api.github.com/").build();
- HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder().exchangeAdapter(WebClientAdapter.forClient(webClient)).build();
+ WebClientAdapter adapter = WebClientAdapter.forClient(webClient)
+ HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
@@ -398,8 +399,9 @@ or using `RestTemplate`:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestTemplate restTemplate = new RestTemplate();
- restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/"));
- HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder().exchangeAdapter(RestTemplateAdapter.forTemplate(restTemplate)).build();
+ restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/"));
+ RestTemplateAdapter adapter = RestTemplateAdapter.forTemplate(restTemplate);
+ HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
----
diff --git a/spring-web/src/main/java/org/springframework/web/client/support/RestTemplateAdapter.java b/spring-web/src/main/java/org/springframework/web/client/support/RestTemplateAdapter.java
index 496b9a133b..214f3f339a 100644
--- a/spring-web/src/main/java/org/springframework/web/client/support/RestTemplateAdapter.java
+++ b/spring-web/src/main/java/org/springframework/web/client/support/RestTemplateAdapter.java
@@ -17,8 +17,9 @@
package org.springframework.web.client.support;
import java.net.URI;
+import java.util.ArrayList;
import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Map;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpCookie;
@@ -27,19 +28,17 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.service.invoker.HttpExchangeAdapter;
import org.springframework.web.service.invoker.HttpRequestValues;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
/**
- * An {@link HttpExchangeAdapter} that enables an {@link HttpServiceProxyFactory} to use
+ * {@link HttpExchangeAdapter} that enables an {@link HttpServiceProxyFactory} to use
* {@link RestTemplate} for request execution.
- *
- * Use static factory methods in this class to create an {@link HttpServiceProxyFactory}
- * configured with a given {@link RestTemplate}.
+ *
+ *
Use static factory methods in this class to create an
+ * {@link HttpServiceProxyFactory} configured with a given {@link RestTemplate}.
*
* @author Olga Maciaszek-Sharma
* @since 6.1
@@ -48,11 +47,17 @@ public final class RestTemplateAdapter implements HttpExchangeAdapter {
private final RestTemplate restTemplate;
- // Private constructor; use static factory methods to instantiate
+
private RestTemplateAdapter(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
+
+ @Override
+ public boolean supportsRequestAttributes() {
+ return false;
+ }
+
@Override
public void exchange(HttpRequestValues requestValues) {
this.restTemplate.exchange(newRequest(requestValues), Void.class);
@@ -74,14 +79,10 @@ public final class RestTemplateAdapter implements HttpExchangeAdapter {
}
@Override
- public ResponseEntity exchangeForEntity(HttpRequestValues requestValues,
- ParameterizedTypeReference bodyType) {
- return this.restTemplate.exchange(newRequest(requestValues), bodyType);
- }
+ public ResponseEntity exchangeForEntity(
+ HttpRequestValues requestValues, ParameterizedTypeReference bodyType) {
- @Override
- public boolean supportsRequestAttributes() {
- return false;
+ return this.restTemplate.exchange(newRequest(requestValues), bodyType);
}
private RequestEntity> newRequest(HttpRequestValues requestValues) {
@@ -90,8 +91,9 @@ public final class RestTemplateAdapter implements HttpExchangeAdapter {
uri = requestValues.getUri();
}
else if (requestValues.getUriTemplate() != null) {
- uri = this.restTemplate.getUriTemplateHandler().expand(requestValues.getUriTemplate(),
- requestValues.getUriVariables());
+ String uriTemplate = requestValues.getUriTemplate();
+ Map variables = requestValues.getUriVariables();
+ uri = this.restTemplate.getUriTemplateHandler().expand(uriTemplate, variables);
}
else {
throw new IllegalStateException("Neither full URL nor URI template");
@@ -100,35 +102,30 @@ public final class RestTemplateAdapter implements HttpExchangeAdapter {
HttpMethod httpMethod = requestValues.getHttpMethod();
Assert.notNull(httpMethod, "HttpMethod is required");
- RequestEntity.BodyBuilder builder = RequestEntity.method(httpMethod, uri)
- .headers(requestValues.getHeaders());
+ RequestEntity.BodyBuilder builder = RequestEntity.method(httpMethod, uri);
+ builder.headers(requestValues.getHeaders());
if (!requestValues.getCookies().isEmpty()) {
- MultiValueMap cookies = new LinkedMultiValueMap<>();
- requestValues.getCookies()
- .forEach((name, values) -> values.forEach(value ->
- cookies.add(name, new HttpCookie(name, value))));
-
- builder.header(HttpHeaders.COOKIE,
- cookies.values()
- .stream()
- .flatMap(List::stream)
- .map(HttpCookie::toString)
- .collect(Collectors.joining("; ")));
+ List cookies = new ArrayList<>();
+ requestValues.getCookies().forEach((name, values) -> values.forEach(value -> {
+ HttpCookie cookie = new HttpCookie(name, value);
+ cookies.add(cookie.toString());
+ }));
+ builder.header(HttpHeaders.COOKIE, String.join("; ", cookies));
}
if (requestValues.getBodyValue() != null) {
return builder.body(requestValues.getBodyValue());
}
+
return builder.build();
}
+
/**
- * Create a {@link RestTemplateAdapter} for the given {@link RestTemplate} instance.
- * @param restTemplate the {@link RestTemplate} to use
- * @return the created adapter instance
+ * Create a {@link RestTemplateAdapter} with the given {@link RestTemplate}.
*/
- public static RestTemplateAdapter forTemplate(RestTemplate restTemplate) {
+ public static RestTemplateAdapter create(RestTemplate restTemplate) {
return new RestTemplateAdapter(restTemplate);
}
diff --git a/spring-web/src/main/java/org/springframework/web/service/invoker/AbstractReactorHttpExchangeAdapter.java b/spring-web/src/main/java/org/springframework/web/service/invoker/AbstractReactorHttpExchangeAdapter.java
index af9cd782a5..653a2b262f 100644
--- a/spring-web/src/main/java/org/springframework/web/service/invoker/AbstractReactorHttpExchangeAdapter.java
+++ b/spring-web/src/main/java/org/springframework/web/service/invoker/AbstractReactorHttpExchangeAdapter.java
@@ -29,10 +29,8 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
- * A base reactive adapter that implements both {@link HttpClientAdapter}
- * and {@link HttpExchangeAdapter}. Allows to ensure backwards compatibility
- * with the deprecated {@link HttpClientAdapter} and handles blocking from reactive
- * publishers to objects where necessary.
+ * Convenient base class for a {@link ReactorHttpExchangeAdapter} implementation
+ * adapting to the synchronous {@link HttpExchangeAdapter} contract.
*
* @author Rossen Stoyanchev
* @since 6.1
@@ -55,30 +53,21 @@ public abstract class AbstractReactorHttpExchangeAdapter
/**
- * Configure the registry for adapting various reactive types.
- * By default this is an instance of {@link ReactiveAdapterRegistry} with
- * default settings.
+ * Configure the {@link ReactiveAdapterRegistry} to use.
+ *
By default, this is {@link ReactiveAdapterRegistry#getSharedInstance()}.
*/
public void setReactiveAdapterRegistry(ReactiveAdapterRegistry reactiveAdapterRegistry) {
this.reactiveAdapterRegistry = reactiveAdapterRegistry;
}
- /**
- * Return the configured reactive type registry of adapters.
- */
@Override
public ReactiveAdapterRegistry getReactiveAdapterRegistry() {
return this.reactiveAdapterRegistry;
}
/**
- * Configure how long to block for the response of an HTTP service method with a
- * synchronous (blocking) method signature.
- *
- * By default, this is not set, in which case the behavior depends on connection and
- * request timeout settings of the underlying HTTP client. We recommend configuring
- * timeout values directly on the underlying HTTP client, which provides more
- * control over such settings.
+ * Configure how long to block for the response of an HTTP service method
+ * as described in {@link #getBlockTimeout()}.
*/
public void setBlockTimeout(@Nullable Duration blockTimeout) {
this.blockTimeout = blockTimeout;
@@ -90,6 +79,7 @@ public abstract class AbstractReactorHttpExchangeAdapter
return this.blockTimeout;
}
+
@Override
public void exchange(HttpRequestValues requestValues) {
if (this.blockTimeout != null) {
@@ -126,7 +116,9 @@ public abstract class AbstractReactorHttpExchangeAdapter
}
@Override
- public ResponseEntity exchangeForEntity(HttpRequestValues requestValues, ParameterizedTypeReference bodyType) {
+ public ResponseEntity exchangeForEntity(
+ HttpRequestValues requestValues, ParameterizedTypeReference bodyType) {
+
ResponseEntity entity = (this.blockTimeout != null ?
exchangeForEntityMono(requestValues, bodyType).block(this.blockTimeout) :
exchangeForEntityMono(requestValues, bodyType).block());
diff --git a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpClientAdapter.java b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpClientAdapter.java
index 1b5aaf587b..75eaeb36fb 100644
--- a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpClientAdapter.java
+++ b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpClientAdapter.java
@@ -91,59 +91,55 @@ public interface HttpClientAdapter {
/**
- * Adapt this {@link HttpClientAdapter} to {@link ReactorHttpExchangeAdapter}.
- * @return a {@link ReactorHttpExchangeAdapter} instance created that delegating to
- * the underlying {@link HttpClientAdapter} implementation
+ * Adapt this instance to {@link ReactorHttpExchangeAdapter}.
* @since 6.1
*/
- default ReactorHttpExchangeAdapter asHttpExchangeAdapter() {
-
- HttpClientAdapter delegate = this;
+ default ReactorHttpExchangeAdapter asReactorExchangeAdapter() {
return new AbstractReactorHttpExchangeAdapter() {
@Override
- public Mono exchangeForMono(HttpRequestValues requestValues) {
- return delegate.requestToVoid(requestValues);
+ public boolean supportsRequestAttributes() {
+ return true;
}
@Override
- public Mono exchangeForHeadersMono(HttpRequestValues requestValues) {
- return delegate.requestToHeaders(requestValues);
+ public Mono exchangeForMono(HttpRequestValues values) {
+ return HttpClientAdapter.this.requestToVoid(values);
}
@Override
- public Mono exchangeForBodyMono(HttpRequestValues requestValues, ParameterizedTypeReference bodyType) {
- return delegate.requestToBody(requestValues, bodyType);
+ public Mono exchangeForHeadersMono(HttpRequestValues values) {
+ return HttpClientAdapter.this.requestToHeaders(values);
}
@Override
- public Flux exchangeForBodyFlux(HttpRequestValues requestValues, ParameterizedTypeReference bodyType) {
- return delegate.requestToBodyFlux(requestValues, bodyType);
+ public Mono exchangeForBodyMono(HttpRequestValues values, ParameterizedTypeReference bodyType) {
+ return HttpClientAdapter.this.requestToBody(values, bodyType);
}
@Override
- public Mono> exchangeForBodilessEntityMono(HttpRequestValues requestValues) {
- return delegate.requestToBodilessEntity(requestValues);
+ public Flux exchangeForBodyFlux(HttpRequestValues values, ParameterizedTypeReference bodyType) {
+ return HttpClientAdapter.this.requestToBodyFlux(values, bodyType);
+ }
+
+ @Override
+ public Mono> exchangeForBodilessEntityMono(HttpRequestValues values) {
+ return HttpClientAdapter.this.requestToBodilessEntity(values);
}
@Override
public Mono> exchangeForEntityMono(
HttpRequestValues requestValues, ParameterizedTypeReference bodyType) {
- return delegate.requestToEntity(requestValues, bodyType);
+ return HttpClientAdapter.this.requestToEntity(requestValues, bodyType);
}
@Override
public Mono>> exchangeForEntityFlux(
HttpRequestValues requestValues, ParameterizedTypeReference bodyType) {
- return delegate.requestToEntityFlux(requestValues, bodyType);
- }
-
- @Override
- public boolean supportsRequestAttributes() {
- return true;
+ return HttpClientAdapter.this.requestToEntityFlux(requestValues, bodyType);
}
};
}
diff --git a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpExchangeAdapter.java b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpExchangeAdapter.java
index 20d995f102..3333e27fbc 100644
--- a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpExchangeAdapter.java
+++ b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpExchangeAdapter.java
@@ -30,6 +30,11 @@ import org.springframework.lang.Nullable;
*/
public interface HttpExchangeAdapter {
+ /**
+ * Whether the underlying client supports use of request attributes.
+ */
+ boolean supportsRequestAttributes();
+
/**
* Perform the given request, and release the response content, if any.
* @param requestValues the request to perform
@@ -49,7 +54,7 @@ public interface HttpExchangeAdapter {
* @param requestValues the request to perform
* @param bodyType the target type to decode to
* @param the type the response is decoded to
- * @return the decoded response.
+ * @return the decoded response body.
*/
@Nullable
T exchangeForBody(HttpRequestValues requestValues, ParameterizedTypeReference bodyType);
@@ -57,18 +62,15 @@ public interface HttpExchangeAdapter {
/**
* Variant of {@link #exchange(HttpRequestValues)} with additional
* access to the response status and headers.
+ * @return the response entity with status and headers.
*/
ResponseEntity exchangeForBodilessEntity(HttpRequestValues requestValues);
/**
* Variant of {@link #exchangeForBody(HttpRequestValues, ParameterizedTypeReference)}
* with additional access to the response status and headers.
+ * @return the response entity with status, headers, and body.
*/
ResponseEntity exchangeForEntity(HttpRequestValues requestValues, ParameterizedTypeReference bodyType);
- /**
- * A flag that indicates whether request attributes are supported by a specific client
- * adapter.
- */
- boolean supportsRequestAttributes();
}
diff --git a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceMethod.java b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceMethod.java
index f60b9c5ee2..18fb67fc9b 100644
--- a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceMethod.java
+++ b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceMethod.java
@@ -80,7 +80,10 @@ final class HttpServiceMethod {
this.method = method;
this.parameters = initMethodParameters(method);
this.argumentResolvers = argumentResolvers;
- this.requestValuesInitializer = HttpRequestValuesInitializer.create(method, containingClass, embeddedValueResolver);
+
+ this.requestValuesInitializer =
+ HttpRequestValuesInitializer.create(method, containingClass, embeddedValueResolver);
+
this.responseFunction =
(REACTOR_PRESENT && adapter instanceof ReactorHttpExchangeAdapter reactorAdapter ?
ReactorExchangeResponseFunction.create(reactorAdapter, method) :
@@ -291,52 +294,54 @@ final class HttpServiceMethod {
return this.responseFunction.apply(requestValues);
}
+
+ /**
+ * Create the {@code ResponseFunction} that matches the method return type.
+ */
public static ResponseFunction create(HttpExchangeAdapter client, Method method) {
if (KotlinDetector.isSuspendingFunction(method)) {
- throw new IllegalStateException("Kotlin Coroutines are only supported with reactive implementations");
+ throw new IllegalStateException(
+ "Kotlin Coroutines are only supported with reactive implementations");
}
- MethodParameter actualReturnParam = new MethodParameter(method, -1).nestedIfOptional();
- boolean returnOptional = actualReturnParam.getParameterType().equals(Optional.class);
- Class> actualReturnType = actualReturnParam.getNestedParameterType();
+
+ MethodParameter param = new MethodParameter(method, -1).nestedIfOptional();
+ Class> paramType = param.getNestedParameterType();
Function responseFunction;
- if (actualReturnType.equals(void.class) || actualReturnType.equals(Void.class)) {
+ if (paramType.equals(void.class) || paramType.equals(Void.class)) {
responseFunction = requestValues -> {
client.exchange(requestValues);
return null;
};
}
- else if (actualReturnType.equals(HttpHeaders.class)) {
- responseFunction = request -> processResponse(client.exchangeForHeaders(request),
- returnOptional);
+ else if (paramType.equals(HttpHeaders.class)) {
+ responseFunction = request -> asOptionalIfNecessary(client.exchangeForHeaders(request), param);
}
- else if (actualReturnType.equals(ResponseEntity.class)) {
- MethodParameter bodyParam = actualReturnParam.nested();
- Class> bodyType = bodyParam.getNestedParameterType();
- if (bodyType.equals(Void.class)) {
- responseFunction = request -> processResponse(client
- .exchangeForBodilessEntity(request), returnOptional);
+ else if (paramType.equals(ResponseEntity.class)) {
+ MethodParameter bodyParam = param.nested();
+ if (bodyParam.getNestedParameterType().equals(Void.class)) {
+ responseFunction = request ->
+ asOptionalIfNecessary(client.exchangeForBodilessEntity(request), param);
}
else {
- ParameterizedTypeReference> bodyTypeReference = ParameterizedTypeReference
- .forType(bodyParam.getNestedGenericParameterType());
- responseFunction = request -> processResponse(client.exchangeForEntity(request,
- bodyTypeReference), returnOptional);
+ ParameterizedTypeReference> bodyTypeRef =
+ ParameterizedTypeReference.forType(bodyParam.getNestedGenericParameterType());
+ responseFunction = request ->
+ asOptionalIfNecessary(client.exchangeForEntity(request, bodyTypeRef), param);
}
}
else {
- ParameterizedTypeReference> bodyTypeReference = ParameterizedTypeReference
- .forType(actualReturnParam.getNestedGenericParameterType());
- responseFunction = request -> processResponse(client.exchangeForBody(request,
- bodyTypeReference), returnOptional);
+ ParameterizedTypeReference> bodyTypeRef =
+ ParameterizedTypeReference.forType(param.getNestedGenericParameterType());
+ responseFunction = request ->
+ asOptionalIfNecessary(client.exchangeForBody(request, bodyTypeRef), param);
}
return new ExchangeResponseFunction(responseFunction);
}
- private static @Nullable Object processResponse(@Nullable Object response,
- boolean returnOptional) {
- return returnOptional ? Optional.ofNullable(response) : response;
+ private static @Nullable Object asOptionalIfNecessary(@Nullable Object response, MethodParameter param) {
+ return param.getParameterType().equals(Optional.class) ? Optional.ofNullable(response) : response;
}
}
@@ -372,7 +377,7 @@ final class HttpServiceMethod {
/**
- * Create the {@code ResponseFunction} that matches the method's return type.
+ * Create the {@code ResponseFunction} that matches the method return type.
*/
public static ResponseFunction create(ReactorHttpExchangeAdapter client, Method method) {
MethodParameter returnParam = new MethodParameter(method, -1);
diff --git a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java
index bbf179307a..6f45ec13d6 100644
--- a/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java
+++ b/spring-web/src/main/java/org/springframework/web/service/invoker/HttpServiceProxyFactory.java
@@ -119,7 +119,7 @@ public final class HttpServiceProxyFactory {
@SuppressWarnings("removal")
@Deprecated(since = "6.1", forRemoval = true)
public static Builder builder(HttpClientAdapter clientAdapter) {
- return new Builder().exchangeAdapter(clientAdapter.asHttpExchangeAdapter());
+ return new Builder().exchangeAdapter(clientAdapter.asReactorExchangeAdapter());
}
/**
@@ -169,7 +169,7 @@ public final class HttpServiceProxyFactory {
@SuppressWarnings("removal")
@Deprecated(since = "6.1", forRemoval = true)
public Builder clientAdapter(HttpClientAdapter clientAdapter) {
- this.exchangeAdapter = clientAdapter.asHttpExchangeAdapter();
+ this.exchangeAdapter = clientAdapter.asReactorExchangeAdapter();
return this;
}
@@ -262,12 +262,12 @@ public final class HttpServiceProxyFactory {
resolvers.add(new RequestHeaderArgumentResolver(service));
resolvers.add(new RequestBodyArgumentResolver());
resolvers.add(new PathVariableArgumentResolver(service));
- if (this.exchangeAdapter.supportsRequestAttributes()) {
- resolvers.add(new RequestAttributeArgumentResolver());
- }
resolvers.add(new RequestParamArgumentResolver(service));
resolvers.add(new RequestPartArgumentResolver());
resolvers.add(new CookieValueArgumentResolver(service));
+ if (this.exchangeAdapter.supportsRequestAttributes()) {
+ resolvers.add(new RequestAttributeArgumentResolver());
+ }
// Specific type
resolvers.add(new UrlArgumentResolver());
diff --git a/spring-web/src/main/java/org/springframework/web/service/invoker/ReactorHttpExchangeAdapter.java b/spring-web/src/main/java/org/springframework/web/service/invoker/ReactorHttpExchangeAdapter.java
index bad465b1f4..47f08906ba 100644
--- a/spring-web/src/main/java/org/springframework/web/service/invoker/ReactorHttpExchangeAdapter.java
+++ b/spring-web/src/main/java/org/springframework/web/service/invoker/ReactorHttpExchangeAdapter.java
@@ -37,19 +37,18 @@ import org.springframework.lang.Nullable;
public interface ReactorHttpExchangeAdapter extends HttpExchangeAdapter {
/**
- * Return the configured reactive type registry of adapters.
+ * Return the configured {@link ReactiveAdapterRegistry}.
*/
ReactiveAdapterRegistry getReactiveAdapterRegistry();
/**
- * Return the configured time to block for the response of an HTTP service method with
- * a synchronous (blocking) method signature.
+ * Return the configured time to block for the response from an HTTP service
+ * method with a synchronous (blocking) method signature.
*
- *
- * By default, this is not set, in which case the behavior depends on connection and
- * request timeout settings of the underlying HTTP client. We recommend configuring
- * timeout values directly on the underlying HTTP client, which provides more *
- * control over such settings.
+ *
By default, not set in which case the behavior depends on connection
+ * and request timeout settings of the underlying HTTP client. We recommend
+ * configuring timeout values directly on the underlying HTTP client, which
+ * provides more control over such settings.
*/
@Nullable
Duration getBlockTimeout();
diff --git a/spring-web/src/test/java/org/springframework/web/client/support/RestTemplateHttpServiceProxyTests.java b/spring-web/src/test/java/org/springframework/web/client/support/RestTemplateAdapterTests.java
similarity index 73%
rename from spring-web/src/test/java/org/springframework/web/client/support/RestTemplateHttpServiceProxyTests.java
rename to spring-web/src/test/java/org/springframework/web/client/support/RestTemplateAdapterTests.java
index b8c382382f..5b36b9e513 100644
--- a/spring-web/src/test/java/org/springframework/web/client/support/RestTemplateHttpServiceProxyTests.java
+++ b/spring-web/src/test/java/org/springframework/web/client/support/RestTemplateAdapterTests.java
@@ -51,31 +51,27 @@ import org.springframework.web.util.DefaultUriBuilderFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
- * Integration tests for {@link HttpServiceProxyFactory HTTP Service proxy} using
- * {@link RestTemplate} and {@link MockWebServer}.
+ * Integration tests for {@link HttpServiceProxyFactory HTTP Service proxy}
+ * with {@link RestTemplateAdapter} connecting to {@link MockWebServer}.
*
* @author Olga Maciaszek-Sharma
*/
-class RestTemplateHttpServiceProxyTests {
+class RestTemplateAdapterTests {
private MockWebServer server;
- private TestService testService;
+ private Service service;
+
@BeforeEach
void setUp() {
this.server = new MockWebServer();
prepareResponse();
- this.testService = initTestService();
- }
- private TestService initTestService() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(this.server.url("/").toString()));
- return HttpServiceProxyFactory.builder()
- .exchangeAdapter(RestTemplateAdapter.forTemplate(restTemplate))
- .build()
- .createClient(TestService.class);
+ RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
+ this.service = HttpServiceProxyFactory.builder().exchangeAdapter(adapter).build().createClient(Service.class);
}
@SuppressWarnings("ConstantConditions")
@@ -86,32 +82,33 @@ class RestTemplateHttpServiceProxyTests {
}
}
+
@Test
- void getRequest() throws InterruptedException {
- String response = testService.getRequest();
+ void greeting() throws InterruptedException {
+ String response = this.service.getGreeting();
RecordedRequest request = this.server.takeRequest();
assertThat(response).isEqualTo("Hello Spring!");
assertThat(request.getMethod()).isEqualTo("GET");
- assertThat(request.getPath()).isEqualTo("/test");
+ assertThat(request.getPath()).isEqualTo("/greeting");
}
@Test
- void getRequestWithPathVariable() throws InterruptedException {
- ResponseEntity response = testService.getRequestWithPathVariable("456");
+ void greetingById() throws InterruptedException {
+ ResponseEntity response = this.service.getGreetingById("456");
RecordedRequest request = this.server.takeRequest();
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isEqualTo("Hello Spring!");
assertThat(request.getMethod()).isEqualTo("GET");
- assertThat(request.getPath()).isEqualTo("/test/456");
+ assertThat(request.getPath()).isEqualTo("/greeting/456");
}
@Test
- void getRequestWithDynamicUri() throws InterruptedException {
+ void greetingWithDynamicUri() throws InterruptedException {
URI dynamicUri = this.server.url("/greeting/123").uri();
- Optional response = testService.getRequestWithDynamicUri(dynamicUri, "456");
+ Optional response = this.service.getGreetingWithDynamicUri(dynamicUri, "456");
RecordedRequest request = this.server.takeRequest();
assertThat(response.orElse("empty")).isEqualTo("Hello Spring!");
@@ -120,12 +117,12 @@ class RestTemplateHttpServiceProxyTests {
}
@Test
- void postWithRequestHeader() throws InterruptedException {
- testService.postRequestWithHeader("testHeader", "testBody");
+ void postWithHeader() throws InterruptedException {
+ service.postWithHeader("testHeader", "testBody");
RecordedRequest request = this.server.takeRequest();
assertThat(request.getMethod()).isEqualTo("POST");
- assertThat(request.getPath()).isEqualTo("/test");
+ assertThat(request.getPath()).isEqualTo("/greeting");
assertThat(request.getHeaders().get("testHeaderName")).isEqualTo("testHeader");
assertThat(request.getBody().readUtf8()).isEqualTo("testBody");
}
@@ -136,7 +133,7 @@ class RestTemplateHttpServiceProxyTests {
map.add("param1", "value 1");
map.add("param2", "value 2");
- testService.postForm(map);
+ service.postForm(map);
RecordedRequest request = this.server.takeRequest();
assertThat(request.getHeaders().get("Content-Type"))
@@ -151,7 +148,7 @@ class RestTemplateHttpServiceProxyTests {
MultipartFile file = new MockMultipartFile(fileName, originalFileName, MediaType.APPLICATION_JSON_VALUE,
"test".getBytes());
- testService.postMultipart(file, "test2");
+ service.postMultipart(file, "test2");
RecordedRequest request = this.server.takeRequest();
assertThat(request.getHeaders().get("Content-Type")).startsWith("multipart/form-data;boundary=");
@@ -163,8 +160,8 @@ class RestTemplateHttpServiceProxyTests {
}
@Test
- void putRequestWithCookies() throws InterruptedException {
- testService.putRequestWithCookies("test1", "test2");
+ void putWithCookies() throws InterruptedException {
+ service.putWithCookies("test1", "test2");
RecordedRequest request = this.server.takeRequest();
assertThat(request.getMethod()).isEqualTo("PUT");
@@ -172,8 +169,8 @@ class RestTemplateHttpServiceProxyTests {
}
@Test
- void putRequestWithSameNameCookies() throws InterruptedException {
- testService.putRequestWithSameNameCookies("test1", "test2");
+ void putWithSameNameCookies() throws InterruptedException {
+ service.putWithSameNameCookies("test1", "test2");
RecordedRequest request = this.server.takeRequest();
assertThat(request.getMethod()).isEqualTo("PUT");
@@ -186,20 +183,21 @@ class RestTemplateHttpServiceProxyTests {
this.server.enqueue(response);
}
- private interface TestService {
- @GetExchange("/test")
- String getRequest();
+ private interface Service {
- @GetExchange("/test/{id}")
- ResponseEntity getRequestWithPathVariable(@PathVariable String id);
+ @GetExchange("/greeting")
+ String getGreeting();
- @GetExchange("/test/{id}")
- Optional getRequestWithDynamicUri(@Nullable URI uri, @PathVariable String id);
+ @GetExchange("/greeting/{id}")
+ ResponseEntity getGreetingById(@PathVariable String id);
- @PostExchange("/test")
- void postRequestWithHeader(@RequestHeader("testHeaderName") String testHeader,
- @RequestBody String requestBody);
+ @GetExchange("/greeting/{id}")
+ Optional getGreetingWithDynamicUri(@Nullable URI uri, @PathVariable String id);
+
+ @PostExchange("/greeting")
+ void postWithHeader(
+ @RequestHeader("testHeaderName") String testHeader, @RequestBody String requestBody);
@PostExchange(contentType = "application/x-www-form-urlencoded")
void postForm(@RequestParam MultiValueMap params);
@@ -208,12 +206,12 @@ class RestTemplateHttpServiceProxyTests {
void postMultipart(MultipartFile file, @RequestPart String anotherPart);
@PutExchange
- void putRequestWithCookies(@CookieValue String firstCookie,
- @CookieValue String secondCookie);
+ void putWithCookies(
+ @CookieValue String firstCookie, @CookieValue String secondCookie);
@PutExchange
- void putRequestWithSameNameCookies(@CookieValue("testCookie") String firstCookie,
- @CookieValue("testCookie") String secondCookie);
+ void putWithSameNameCookies(
+ @CookieValue("testCookie") String firstCookie, @CookieValue("testCookie") String secondCookie);
}
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/CookieValueArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/CookieValueArgumentResolverTests.java
index 27ed4bf9d7..d0e3bfaf2d 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/CookieValueArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/CookieValueArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/HttpMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/HttpMethodArgumentResolverTests.java
index d880cc5c85..d060b550af 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/HttpMethodArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/HttpMethodArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/NamedValueArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/NamedValueArgumentResolverTests.java
index 96f3a568d3..5b4b571b9e 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/NamedValueArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/NamedValueArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/PathVariableArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/PathVariableArgumentResolverTests.java
index 0cfaf6fbb2..4fdb1f08e5 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/PathVariableArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/PathVariableArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/RequestAttributeArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/RequestAttributeArgumentResolverTests.java
index 5107ed8b00..51095802ef 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/RequestAttributeArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/RequestAttributeArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/RequestBodyArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/RequestBodyArgumentResolverTests.java
index 55d56dc1ad..ca41cead54 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/RequestBodyArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/RequestBodyArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/RequestHeaderArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/RequestHeaderArgumentResolverTests.java
index 4bf60d8b44..d7d1dd2f0c 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/RequestHeaderArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/RequestHeaderArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/java/org/springframework/web/service/invoker/UrlArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/service/invoker/UrlArgumentResolverTests.java
index 210c411bdc..2819ec459e 100644
--- a/spring-web/src/test/java/org/springframework/web/service/invoker/UrlArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/service/invoker/UrlArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/spring-web/src/test/kotlin/org/springframework/web/client/support/KotlinRestTemplateHttpServiceProxyTests.kt b/spring-web/src/test/kotlin/org/springframework/web/client/support/KotlinRestTemplateHttpServiceProxyTests.kt
index 8d3e36f38a..82bba493ea 100644
--- a/spring-web/src/test/kotlin/org/springframework/web/client/support/KotlinRestTemplateHttpServiceProxyTests.kt
+++ b/spring-web/src/test/kotlin/org/springframework/web/client/support/KotlinRestTemplateHttpServiceProxyTests.kt
@@ -63,7 +63,7 @@ class KotlinRestTemplateHttpServiceProxyTests {
val restTemplate = RestTemplate()
restTemplate.uriTemplateHandler = DefaultUriBuilderFactory(server.url("/").toString())
return HttpServiceProxyFactory.builder()
- .exchangeAdapter(RestTemplateAdapter.forTemplate(restTemplate))
+ .exchangeAdapter(RestTemplateAdapter.create(restTemplate))
.build()
.createClient(TestService::class.java)
}
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 2a9c99f4e6..5a058b0608 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
@@ -53,6 +53,11 @@ public final class WebClientAdapter extends AbstractReactorHttpExchangeAdapter {
}
+ @Override
+ public boolean supportsRequestAttributes() {
+ return true;
+ }
+
@Override
public Mono exchangeForMono(HttpRequestValues requestValues) {
return newRequest(requestValues).retrieve().toBodilessEntity().then();
@@ -132,8 +137,4 @@ public final class WebClientAdapter extends AbstractReactorHttpExchangeAdapter {
return new WebClientAdapter(webClient);
}
- @Override
- public boolean supportsRequestAttributes() {
- return true;
- }
}
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientHttpServiceProxyTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java
similarity index 90%
rename from spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientHttpServiceProxyTests.java
rename to spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java
index bd11b8dbe6..42bbc08659 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientHttpServiceProxyTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/support/WebClientAdapterTests.java
@@ -53,12 +53,12 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link HttpServiceProxyFactory HTTP Service proxy}
- * using {@link WebClient} and {@link MockWebServer}.
+ * with {@link WebClientAdapter} connecting to {@link MockWebServer}.
*
* @author Rossen Stoyanchev
* @author Olga Maciaszek-Sharma
*/
-public class WebClientHttpServiceProxyTests {
+public class WebClientAdapterTests {
private MockWebServer server;
@@ -82,7 +82,7 @@ public class WebClientHttpServiceProxyTests {
prepareResponse(response ->
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
- StepVerifier.create(initHttpService().getGreeting())
+ StepVerifier.create(initService().getGreeting())
.expectNext("Hello Spring!")
.expectComplete()
.verify(Duration.ofSeconds(5));
@@ -103,7 +103,7 @@ public class WebClientHttpServiceProxyTests {
prepareResponse(response ->
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
- StepVerifier.create(initHttpService(webClient).getGreetingWithAttribute("myAttributeValue"))
+ StepVerifier.create(initService(webClient).getGreetingWithAttribute("myAttributeValue"))
.expectNext("Hello Spring!")
.expectComplete()
.verify(Duration.ofSeconds(5));
@@ -117,7 +117,7 @@ public class WebClientHttpServiceProxyTests {
prepareResponse(response -> response.setResponseCode(200).setBody(expectedBody));
URI dynamicUri = this.server.url("/greeting/123").uri();
- String actualBody = initHttpService().getGreetingById(dynamicUri, "456");
+ String actualBody = initService().getGreetingById(dynamicUri, "456");
assertThat(actualBody).isEqualTo(expectedBody);
assertThat(this.server.takeRequest().getRequestUrl().uri()).isEqualTo(dynamicUri);
@@ -131,7 +131,7 @@ public class WebClientHttpServiceProxyTests {
map.add("param1", "value 1");
map.add("param2", "value 2");
- initHttpService().postForm(map);
+ initService().postForm(map);
RecordedRequest request = this.server.takeRequest();
assertThat(request.getHeaders().get("Content-Type")).isEqualTo("application/x-www-form-urlencoded;charset=UTF-8");
@@ -146,7 +146,7 @@ public class WebClientHttpServiceProxyTests {
MultipartFile file = new MockMultipartFile(fileName, originalFileName,
MediaType.APPLICATION_JSON_VALUE, "test".getBytes());
- initHttpService().postMultipart(file, "test2");
+ initService().postMultipart(file, "test2");
RecordedRequest request = this.server.takeRequest();
assertThat(request.getHeaders().get("Content-Type")).startsWith("multipart/form-data;boundary=");
@@ -157,14 +157,14 @@ public class WebClientHttpServiceProxyTests {
"Content-Type: text/plain;charset=UTF-8", "Content-Length: 5", "test2");
}
- private TestHttpService initHttpService() {
+ private Service initService() {
WebClient webClient = WebClient.builder().baseUrl(this.server.url("/").toString()).build();
- return initHttpService(webClient);
+ return initService(webClient);
}
- private TestHttpService initHttpService(WebClient webClient) {
+ private Service initService(WebClient webClient) {
WebClientAdapter adapter = WebClientAdapter.forClient(webClient);
- return HttpServiceProxyFactory.builderFor(adapter).build().createClient(TestHttpService.class);
+ return HttpServiceProxyFactory.builderFor(adapter).build().createClient(Service.class);
}
private void prepareResponse(Consumer consumer) {
@@ -174,7 +174,7 @@ public class WebClientHttpServiceProxyTests {
}
- private interface TestHttpService {
+ private interface Service {
@GetExchange("/greeting")
Mono getGreeting();
diff --git a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/support/WebClientHttpServiceProxyKotlinTests.kt b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/support/WebClientHttpServiceProxyKotlinTests.kt
index 9292ade029..7ca2816cc7 100644
--- a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/support/WebClientHttpServiceProxyKotlinTests.kt
+++ b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/function/client/support/WebClientHttpServiceProxyKotlinTests.kt
@@ -128,10 +128,8 @@ class KotlinWebClientHttpServiceProxyTests {
}
private fun initHttpService(webClient: WebClient): TestHttpService {
- return HttpServiceProxyFactory.builder()
- .clientAdapter(WebClientAdapter.forClient(webClient))
- .build()
- .createClient()
+ val adapter = WebClientAdapter.forClient(webClient)
+ return HttpServiceProxyFactory.builderFor(adapter).build().createClient()
}
private fun prepareResponse(consumer: Consumer) {