parent
8b77ed164d
commit
57ed5bf34b
|
@ -31,24 +31,26 @@ import org.springframework.web.service.invoker.HttpRequestValues;
|
||||||
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link HttpExchangeAdapter} that enables an {@link HttpServiceProxyFactory} to use
|
* {@link HttpExchangeAdapter} that enables an {@link HttpServiceProxyFactory}
|
||||||
* {@link RestClient} for request execution.
|
* to use {@link RestClient} for request execution.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>Use static factory methods in this class to create an
|
||||||
* Use static factory methods in this class to create an {@link HttpServiceProxyFactory}
|
* {@link HttpServiceProxyFactory} configured with the given {@link RestClient}.
|
||||||
* configured with a given {@link RestClient}.
|
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
* @since 6.1
|
* @since 6.1
|
||||||
*/
|
*/
|
||||||
public final class RestClientAdapter implements HttpExchangeAdapter {
|
public final class RestClientAdapter implements HttpExchangeAdapter {
|
||||||
|
|
||||||
private final RestClient restClient;
|
private final RestClient restClient;
|
||||||
|
|
||||||
|
|
||||||
private RestClientAdapter(RestClient restClient) {
|
private RestClientAdapter(RestClient restClient) {
|
||||||
this.restClient = restClient;
|
this.restClient = restClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsRequestAttributes() {
|
public boolean supportsRequestAttributes() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -60,66 +62,66 @@ public final class RestClientAdapter implements HttpExchangeAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpHeaders exchangeForHeaders(HttpRequestValues requestValues) {
|
public HttpHeaders exchangeForHeaders(HttpRequestValues values) {
|
||||||
return newRequest(requestValues).retrieve().toBodilessEntity().getHeaders();
|
return newRequest(values).retrieve().toBodilessEntity().getHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T exchangeForBody(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
|
public <T> T exchangeForBody(HttpRequestValues values, ParameterizedTypeReference<T> bodyType) {
|
||||||
return newRequest(requestValues).retrieve().body(bodyType);
|
return newRequest(values).retrieve().body(bodyType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Void> exchangeForBodilessEntity(HttpRequestValues requestValues) {
|
public ResponseEntity<Void> exchangeForBodilessEntity(HttpRequestValues values) {
|
||||||
return newRequest(requestValues).retrieve().toBodilessEntity();
|
return newRequest(values).retrieve().toBodilessEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> ResponseEntity<T> exchangeForEntity(HttpRequestValues requestValues,
|
public <T> ResponseEntity<T> exchangeForEntity(HttpRequestValues values, ParameterizedTypeReference<T> bodyType) {
|
||||||
ParameterizedTypeReference<T> bodyType) {
|
return newRequest(values).retrieve().toEntity(bodyType);
|
||||||
return newRequest(requestValues).retrieve().toEntity(bodyType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RestClient.RequestBodySpec newRequest(HttpRequestValues requestValues) {
|
private RestClient.RequestBodySpec newRequest(HttpRequestValues values) {
|
||||||
|
|
||||||
HttpMethod httpMethod = requestValues.getHttpMethod();
|
HttpMethod httpMethod = values.getHttpMethod();
|
||||||
Assert.notNull(httpMethod, "HttpMethod is required");
|
Assert.notNull(httpMethod, "HttpMethod is required");
|
||||||
|
|
||||||
RestClient.RequestBodyUriSpec uriSpec = this.restClient.method(httpMethod);
|
RestClient.RequestBodyUriSpec uriSpec = this.restClient.method(httpMethod);
|
||||||
|
|
||||||
RestClient.RequestBodySpec bodySpec;
|
RestClient.RequestBodySpec bodySpec;
|
||||||
if (requestValues.getUri() != null) {
|
if (values.getUri() != null) {
|
||||||
bodySpec = uriSpec.uri(requestValues.getUri());
|
bodySpec = uriSpec.uri(values.getUri());
|
||||||
}
|
}
|
||||||
else if (requestValues.getUriTemplate() != null) {
|
else if (values.getUriTemplate() != null) {
|
||||||
bodySpec = uriSpec.uri(requestValues.getUriTemplate(), requestValues.getUriVariables());
|
bodySpec = uriSpec.uri(values.getUriTemplate(), values.getUriVariables());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("Neither full URL nor URI template");
|
throw new IllegalStateException("Neither full URL nor URI template");
|
||||||
}
|
}
|
||||||
|
|
||||||
bodySpec.headers(headers -> headers.putAll(requestValues.getHeaders()));
|
bodySpec.headers(headers -> headers.putAll(values.getHeaders()));
|
||||||
|
|
||||||
if (!requestValues.getCookies().isEmpty()) {
|
if (!values.getCookies().isEmpty()) {
|
||||||
List<String> cookies = new ArrayList<>();
|
List<String> cookies = new ArrayList<>();
|
||||||
requestValues.getCookies().forEach((name, values) -> values.forEach(value -> {
|
values.getCookies().forEach((name, cookieValues) -> cookieValues.forEach(value -> {
|
||||||
HttpCookie cookie = new HttpCookie(name, value);
|
HttpCookie cookie = new HttpCookie(name, value);
|
||||||
cookies.add(cookie.toString());
|
cookies.add(cookie.toString());
|
||||||
}));
|
}));
|
||||||
bodySpec.header(HttpHeaders.COOKIE, String.join("; ", cookies));
|
bodySpec.header(HttpHeaders.COOKIE, String.join("; ", cookies));
|
||||||
}
|
}
|
||||||
|
|
||||||
bodySpec.attributes(attributes -> attributes.putAll(requestValues.getAttributes()));
|
bodySpec.attributes(attributes -> attributes.putAll(values.getAttributes()));
|
||||||
|
|
||||||
if (requestValues.getBodyValue() != null) {
|
if (values.getBodyValue() != null) {
|
||||||
bodySpec.body(requestValues.getBodyValue());
|
bodySpec.body(values.getBodyValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return bodySpec;
|
return bodySpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link RestClientAdapter} with the given {@link RestClient}.
|
* Create a {@link RestClientAdapter} for the given {@link RestClient}.
|
||||||
*/
|
*/
|
||||||
public static RestClientAdapter create(RestClient restClient) {
|
public static RestClientAdapter create(RestClient restClient) {
|
||||||
return new RestClientAdapter(restClient);
|
return new RestClientAdapter(restClient);
|
||||||
|
|
|
@ -34,11 +34,11 @@ import org.springframework.web.service.invoker.HttpRequestValues;
|
||||||
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link HttpExchangeAdapter} that enables an {@link HttpServiceProxyFactory} to use
|
* {@link HttpExchangeAdapter} that enables an {@link HttpServiceProxyFactory}
|
||||||
* {@link RestTemplate} for request execution.
|
* to use {@link RestTemplate} for request execution.
|
||||||
*
|
*
|
||||||
* <p>Use static factory methods in this class to create an
|
* <p>Use static factory methods in this class to create an
|
||||||
* {@link HttpServiceProxyFactory} configured with a given {@link RestTemplate}.
|
* {@link HttpServiceProxyFactory} configured with the given {@link RestTemplate}.
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @since 6.1
|
* @since 6.1
|
||||||
|
@ -59,63 +59,61 @@ public final class RestTemplateAdapter implements HttpExchangeAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exchange(HttpRequestValues requestValues) {
|
public void exchange(HttpRequestValues values) {
|
||||||
this.restTemplate.exchange(newRequest(requestValues), Void.class);
|
this.restTemplate.exchange(newRequest(values), Void.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpHeaders exchangeForHeaders(HttpRequestValues requestValues) {
|
public HttpHeaders exchangeForHeaders(HttpRequestValues values) {
|
||||||
return this.restTemplate.exchange(newRequest(requestValues), Void.class).getHeaders();
|
return this.restTemplate.exchange(newRequest(values), Void.class).getHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T exchangeForBody(HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
|
public <T> T exchangeForBody(HttpRequestValues values, ParameterizedTypeReference<T> bodyType) {
|
||||||
return this.restTemplate.exchange(newRequest(requestValues), bodyType).getBody();
|
return this.restTemplate.exchange(newRequest(values), bodyType).getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Void> exchangeForBodilessEntity(HttpRequestValues requestValues) {
|
public ResponseEntity<Void> exchangeForBodilessEntity(HttpRequestValues values) {
|
||||||
return this.restTemplate.exchange(newRequest(requestValues), Void.class);
|
return this.restTemplate.exchange(newRequest(values), Void.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> ResponseEntity<T> exchangeForEntity(
|
public <T> ResponseEntity<T> exchangeForEntity(HttpRequestValues values, ParameterizedTypeReference<T> bodyType) {
|
||||||
HttpRequestValues requestValues, ParameterizedTypeReference<T> bodyType) {
|
return this.restTemplate.exchange(newRequest(values), bodyType);
|
||||||
|
|
||||||
return this.restTemplate.exchange(newRequest(requestValues), bodyType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestEntity<?> newRequest(HttpRequestValues requestValues) {
|
private RequestEntity<?> newRequest(HttpRequestValues values) {
|
||||||
URI uri;
|
URI uri;
|
||||||
if (requestValues.getUri() != null) {
|
if (values.getUri() != null) {
|
||||||
uri = requestValues.getUri();
|
uri = values.getUri();
|
||||||
}
|
}
|
||||||
else if (requestValues.getUriTemplate() != null) {
|
else if (values.getUriTemplate() != null) {
|
||||||
String uriTemplate = requestValues.getUriTemplate();
|
String uriTemplate = values.getUriTemplate();
|
||||||
Map<String, String> variables = requestValues.getUriVariables();
|
Map<String, String> variables = values.getUriVariables();
|
||||||
uri = this.restTemplate.getUriTemplateHandler().expand(uriTemplate, variables);
|
uri = this.restTemplate.getUriTemplateHandler().expand(uriTemplate, variables);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("Neither full URL nor URI template");
|
throw new IllegalStateException("Neither full URL nor URI template");
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpMethod httpMethod = requestValues.getHttpMethod();
|
HttpMethod httpMethod = values.getHttpMethod();
|
||||||
Assert.notNull(httpMethod, "HttpMethod is required");
|
Assert.notNull(httpMethod, "HttpMethod is required");
|
||||||
|
|
||||||
RequestEntity.BodyBuilder builder = RequestEntity.method(httpMethod, uri);
|
RequestEntity.BodyBuilder builder = RequestEntity.method(httpMethod, uri);
|
||||||
builder.headers(requestValues.getHeaders());
|
builder.headers(values.getHeaders());
|
||||||
|
|
||||||
if (!requestValues.getCookies().isEmpty()) {
|
if (!values.getCookies().isEmpty()) {
|
||||||
List<String> cookies = new ArrayList<>();
|
List<String> cookies = new ArrayList<>();
|
||||||
requestValues.getCookies().forEach((name, values) -> values.forEach(value -> {
|
values.getCookies().forEach((name, cookieValues) -> cookieValues.forEach(value -> {
|
||||||
HttpCookie cookie = new HttpCookie(name, value);
|
HttpCookie cookie = new HttpCookie(name, value);
|
||||||
cookies.add(cookie.toString());
|
cookies.add(cookie.toString());
|
||||||
}));
|
}));
|
||||||
builder.header(HttpHeaders.COOKIE, String.join("; ", cookies));
|
builder.header(HttpHeaders.COOKIE, String.join("; ", cookies));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestValues.getBodyValue() != null) {
|
if (values.getBodyValue() != null) {
|
||||||
return builder.body(requestValues.getBodyValue());
|
return builder.body(values.getBodyValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
@ -123,7 +121,7 @@ public final class RestTemplateAdapter implements HttpExchangeAdapter {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link RestTemplateAdapter} with the given {@link RestTemplate}.
|
* Create a {@link RestTemplateAdapter} for the given {@link RestTemplate}.
|
||||||
*/
|
*/
|
||||||
public static RestTemplateAdapter create(RestTemplate restTemplate) {
|
public static RestTemplateAdapter create(RestTemplate restTemplate) {
|
||||||
return new RestTemplateAdapter(restTemplate);
|
return new RestTemplateAdapter(restTemplate);
|
||||||
|
|
|
@ -53,9 +53,9 @@ import org.springframework.web.service.annotation.HttpExchange;
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 6.0
|
* @since 6.0
|
||||||
* @see org.springframework.web.client.support.RestTemplateAdapter
|
|
||||||
* @see org.springframework.web.reactive.function.client.support.WebClientAdapter
|
|
||||||
* @see org.springframework.web.client.support.RestClientAdapter
|
* @see org.springframework.web.client.support.RestClientAdapter
|
||||||
|
* @see org.springframework.web.reactive.function.client.support.WebClientAdapter
|
||||||
|
* @see org.springframework.web.client.support.RestTemplateAdapter
|
||||||
*/
|
*/
|
||||||
public final class HttpServiceProxyFactory {
|
public final class HttpServiceProxyFactory {
|
||||||
|
|
||||||
|
|
|
@ -16,17 +16,21 @@
|
||||||
|
|
||||||
package org.springframework.web.client.support;
|
package org.springframework.web.client.support;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import okhttp3.mockwebserver.MockResponse;
|
import okhttp3.mockwebserver.MockResponse;
|
||||||
import okhttp3.mockwebserver.MockWebServer;
|
import okhttp3.mockwebserver.MockWebServer;
|
||||||
import okhttp3.mockwebserver.RecordedRequest;
|
import okhttp3.mockwebserver.RecordedRequest;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
|
import org.springframework.cglib.core.internal.Function;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
@ -40,113 +44,127 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RequestPart;
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
import org.springframework.web.client.RestClient;
|
import org.springframework.web.client.RestClient;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.springframework.web.service.annotation.GetExchange;
|
import org.springframework.web.service.annotation.GetExchange;
|
||||||
import org.springframework.web.service.annotation.PostExchange;
|
import org.springframework.web.service.annotation.PostExchange;
|
||||||
import org.springframework.web.service.annotation.PutExchange;
|
import org.springframework.web.service.annotation.PutExchange;
|
||||||
|
import org.springframework.web.service.invoker.HttpExchangeAdapter;
|
||||||
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
||||||
import org.springframework.web.testfixture.servlet.MockMultipartFile;
|
import org.springframework.web.testfixture.servlet.MockMultipartFile;
|
||||||
|
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for {@link HttpServiceProxyFactory HTTP Service proxy} with
|
* Integration tests for {@link HttpServiceProxyFactory} with {@link RestClient}
|
||||||
* {@link RestClientAdapter} connecting to {@link MockWebServer}.
|
* and {@link RestTemplate} connecting to {@link MockWebServer}.
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("JUnitMalformedDeclaration")
|
||||||
class RestClientAdapterTests {
|
class RestClientAdapterTests {
|
||||||
|
|
||||||
private MockWebServer server;
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
private Service service;
|
@ParameterizedTest
|
||||||
|
@MethodSource("arguments")
|
||||||
@BeforeEach
|
@interface ParameterizedAdapterTest {
|
||||||
void setUp() {
|
|
||||||
this.server = new MockWebServer();
|
|
||||||
prepareResponse();
|
|
||||||
|
|
||||||
RestClient restClient = RestClient.builder().baseUrl(this.server.url("/").toString()).build();
|
|
||||||
RestClientAdapter adapter = RestClientAdapter.create(restClient);
|
|
||||||
this.service = HttpServiceProxyFactory.builderFor(adapter).build().createClient(Service.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
public static Stream<Object[]> arguments() {
|
||||||
@AfterEach
|
return Stream.of(
|
||||||
void shutDown() throws IOException {
|
args(url -> {
|
||||||
if (this.server != null) {
|
RestClient restClient = RestClient.builder().baseUrl(url).build();
|
||||||
this.server.shutdown();
|
return RestClientAdapter.create(restClient);
|
||||||
}
|
}),
|
||||||
|
args(url -> {
|
||||||
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(url));
|
||||||
|
return RestTemplateAdapter.create(restTemplate);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@SuppressWarnings("resource")
|
||||||
void greeting() throws InterruptedException {
|
private static Object[] args(Function<String, HttpExchangeAdapter> adapterFactory) {
|
||||||
String response = this.service.getGreeting();
|
MockWebServer server = new MockWebServer();
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
MockResponse response = new MockResponse();
|
||||||
|
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!");
|
||||||
|
server.enqueue(response);
|
||||||
|
|
||||||
|
HttpExchangeAdapter adapter = adapterFactory.apply(server.url("/").toString());
|
||||||
|
Service service = HttpServiceProxyFactory.builderFor(adapter).build().createClient(Service.class);
|
||||||
|
|
||||||
|
return new Object[] { server, service };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ParameterizedAdapterTest
|
||||||
|
void greeting(MockWebServer server, Service service) throws Exception {
|
||||||
|
String response = service.getGreeting();
|
||||||
|
|
||||||
|
RecordedRequest request = server.takeRequest();
|
||||||
assertThat(response).isEqualTo("Hello Spring!");
|
assertThat(response).isEqualTo("Hello Spring!");
|
||||||
assertThat(request.getMethod()).isEqualTo("GET");
|
assertThat(request.getMethod()).isEqualTo("GET");
|
||||||
assertThat(request.getPath()).isEqualTo("/greeting");
|
assertThat(request.getPath()).isEqualTo("/greeting");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedAdapterTest
|
||||||
void greetingById() throws InterruptedException {
|
void greetingById(MockWebServer server, Service service) throws Exception {
|
||||||
ResponseEntity<String> response = this.service.getGreetingById("456");
|
ResponseEntity<String> response = service.getGreetingById("456");
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
RecordedRequest request = server.takeRequest();
|
||||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||||
assertThat(response.getBody()).isEqualTo("Hello Spring!");
|
assertThat(response.getBody()).isEqualTo("Hello Spring!");
|
||||||
assertThat(request.getMethod()).isEqualTo("GET");
|
assertThat(request.getMethod()).isEqualTo("GET");
|
||||||
assertThat(request.getPath()).isEqualTo("/greeting/456");
|
assertThat(request.getPath()).isEqualTo("/greeting/456");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedAdapterTest
|
||||||
void greetingWithDynamicUri() throws InterruptedException {
|
void greetingWithDynamicUri(MockWebServer server, Service service) throws Exception {
|
||||||
URI dynamicUri = this.server.url("/greeting/123").uri();
|
URI dynamicUri = server.url("/greeting/123").uri();
|
||||||
|
Optional<String> response = service.getGreetingWithDynamicUri(dynamicUri, "456");
|
||||||
|
|
||||||
Optional<String> response = this.service.getGreetingWithDynamicUri(dynamicUri, "456");
|
RecordedRequest request = server.takeRequest();
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
|
||||||
assertThat(response.orElse("empty")).isEqualTo("Hello Spring!");
|
assertThat(response.orElse("empty")).isEqualTo("Hello Spring!");
|
||||||
assertThat(request.getMethod()).isEqualTo("GET");
|
assertThat(request.getMethod()).isEqualTo("GET");
|
||||||
assertThat(request.getRequestUrl().uri()).isEqualTo(dynamicUri);
|
assertThat(request.getRequestUrl().uri()).isEqualTo(dynamicUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedAdapterTest
|
||||||
void postWithHeader() throws InterruptedException {
|
void postWithHeader(MockWebServer server, Service service) throws Exception {
|
||||||
service.postWithHeader("testHeader", "testBody");
|
service.postWithHeader("testHeader", "testBody");
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
RecordedRequest request = server.takeRequest();
|
||||||
assertThat(request.getMethod()).isEqualTo("POST");
|
assertThat(request.getMethod()).isEqualTo("POST");
|
||||||
assertThat(request.getPath()).isEqualTo("/greeting");
|
assertThat(request.getPath()).isEqualTo("/greeting");
|
||||||
assertThat(request.getHeaders().get("testHeaderName")).isEqualTo("testHeader");
|
assertThat(request.getHeaders().get("testHeaderName")).isEqualTo("testHeader");
|
||||||
assertThat(request.getBody().readUtf8()).isEqualTo("testBody");
|
assertThat(request.getBody().readUtf8()).isEqualTo("testBody");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedAdapterTest
|
||||||
void formData() throws Exception {
|
void formData(MockWebServer server, Service service) throws Exception {
|
||||||
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
||||||
map.add("param1", "value 1");
|
map.add("param1", "value 1");
|
||||||
map.add("param2", "value 2");
|
map.add("param2", "value 2");
|
||||||
|
|
||||||
service.postForm(map);
|
service.postForm(map);
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
RecordedRequest request = server.takeRequest();
|
||||||
assertThat(request.getHeaders().get("Content-Type"))
|
assertThat(request.getHeaders().get("Content-Type")).isEqualTo("application/x-www-form-urlencoded;charset=UTF-8");
|
||||||
.isEqualTo("application/x-www-form-urlencoded;charset=UTF-8");
|
|
||||||
assertThat(request.getBody().readUtf8()).isEqualTo("param1=value+1¶m2=value+2");
|
assertThat(request.getBody().readUtf8()).isEqualTo("param1=value+1¶m2=value+2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // gh-30342
|
@ParameterizedAdapterTest // gh-30342
|
||||||
void multipart() throws InterruptedException {
|
void multipart(MockWebServer server, Service service) throws Exception {
|
||||||
String fileName = "testFileName";
|
MultipartFile file = new MockMultipartFile(
|
||||||
String originalFileName = "originalTestFileName";
|
"testFileName", "originalTestFileName", MediaType.APPLICATION_JSON_VALUE, "test".getBytes());
|
||||||
MultipartFile file = new MockMultipartFile(fileName, originalFileName, MediaType.APPLICATION_JSON_VALUE,
|
|
||||||
"test".getBytes());
|
|
||||||
|
|
||||||
service.postMultipart(file, "test2");
|
service.postMultipart(file, "test2");
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
RecordedRequest request = server.takeRequest();
|
||||||
assertThat(request.getHeaders().get("Content-Type")).startsWith("multipart/form-data;boundary=");
|
assertThat(request.getHeaders().get("Content-Type")).startsWith("multipart/form-data;boundary=");
|
||||||
assertThat(request.getBody().readUtf8()).containsSubsequence(
|
assertThat(request.getBody().readUtf8()).containsSubsequence(
|
||||||
"Content-Disposition: form-data; name=\"file\"; filename=\"originalTestFileName\"",
|
"Content-Disposition: form-data; name=\"file\"; filename=\"originalTestFileName\"",
|
||||||
|
@ -155,29 +173,24 @@ class RestClientAdapterTests {
|
||||||
"Content-Length: 5", "test2");
|
"Content-Length: 5", "test2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedAdapterTest
|
||||||
void putWithCookies() throws InterruptedException {
|
void putWithCookies(MockWebServer server, Service service) throws Exception {
|
||||||
service.putWithCookies("test1", "test2");
|
service.putWithCookies("test1", "test2");
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
RecordedRequest request = server.takeRequest();
|
||||||
assertThat(request.getMethod()).isEqualTo("PUT");
|
assertThat(request.getMethod()).isEqualTo("PUT");
|
||||||
assertThat(request.getHeader("Cookie")).isEqualTo("firstCookie=test1; secondCookie=test2");
|
assertThat(request.getHeader("Cookie")).isEqualTo("firstCookie=test1; secondCookie=test2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@ParameterizedAdapterTest
|
||||||
void putWithSameNameCookies() throws InterruptedException {
|
void putWithSameNameCookies(MockWebServer server, Service service) throws Exception {
|
||||||
service.putWithSameNameCookies("test1", "test2");
|
service.putWithSameNameCookies("test1", "test2");
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
RecordedRequest request = server.takeRequest();
|
||||||
assertThat(request.getMethod()).isEqualTo("PUT");
|
assertThat(request.getMethod()).isEqualTo("PUT");
|
||||||
assertThat(request.getHeader("Cookie")).isEqualTo("testCookie=test1; testCookie=test2");
|
assertThat(request.getHeader("Cookie")).isEqualTo("testCookie=test1; testCookie=test2");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareResponse() {
|
|
||||||
MockResponse response = new MockResponse();
|
|
||||||
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!");
|
|
||||||
this.server.enqueue(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface Service {
|
private interface Service {
|
||||||
|
|
||||||
|
@ -203,8 +216,8 @@ class RestClientAdapterTests {
|
||||||
void putWithCookies(@CookieValue String firstCookie, @CookieValue String secondCookie);
|
void putWithCookies(@CookieValue String firstCookie, @CookieValue String secondCookie);
|
||||||
|
|
||||||
@PutExchange
|
@PutExchange
|
||||||
void putWithSameNameCookies(@CookieValue("testCookie") String firstCookie,
|
void putWithSameNameCookies(
|
||||||
@CookieValue("testCookie") String secondCookie);
|
@CookieValue("testCookie") String firstCookie, @CookieValue("testCookie") String secondCookie);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.web.client.support;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import okhttp3.mockwebserver.MockResponse;
|
|
||||||
import okhttp3.mockwebserver.MockWebServer;
|
|
||||||
import okhttp3.mockwebserver.RecordedRequest;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
|
||||||
import org.springframework.lang.Nullable;
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
import org.springframework.web.bind.annotation.CookieValue;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestHeader;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RequestPart;
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
import org.springframework.web.service.annotation.GetExchange;
|
|
||||||
import org.springframework.web.service.annotation.PostExchange;
|
|
||||||
import org.springframework.web.service.annotation.PutExchange;
|
|
||||||
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
|
|
||||||
import org.springframework.web.testfixture.servlet.MockMultipartFile;
|
|
||||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Integration tests for {@link HttpServiceProxyFactory HTTP Service proxy}
|
|
||||||
* with {@link RestTemplateAdapter} connecting to {@link MockWebServer}.
|
|
||||||
*
|
|
||||||
* @author Olga Maciaszek-Sharma
|
|
||||||
*/
|
|
||||||
class RestTemplateAdapterTests {
|
|
||||||
|
|
||||||
private MockWebServer server;
|
|
||||||
|
|
||||||
private Service service;
|
|
||||||
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void setUp() {
|
|
||||||
this.server = new MockWebServer();
|
|
||||||
prepareResponse();
|
|
||||||
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
|
||||||
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(this.server.url("/").toString()));
|
|
||||||
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
|
|
||||||
this.service = HttpServiceProxyFactory.builder().exchangeAdapter(adapter).build().createClient(Service.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
@AfterEach
|
|
||||||
void shutDown() throws IOException {
|
|
||||||
if (this.server != null) {
|
|
||||||
this.server.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
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("/greeting");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void greetingById() throws InterruptedException {
|
|
||||||
ResponseEntity<String> 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("/greeting/456");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void greetingWithDynamicUri() throws InterruptedException {
|
|
||||||
URI dynamicUri = this.server.url("/greeting/123").uri();
|
|
||||||
|
|
||||||
Optional<String> response = this.service.getGreetingWithDynamicUri(dynamicUri, "456");
|
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
|
||||||
assertThat(response.orElse("empty")).isEqualTo("Hello Spring!");
|
|
||||||
assertThat(request.getMethod()).isEqualTo("GET");
|
|
||||||
assertThat(request.getRequestUrl().uri()).isEqualTo(dynamicUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void postWithHeader() throws InterruptedException {
|
|
||||||
service.postWithHeader("testHeader", "testBody");
|
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
|
||||||
assertThat(request.getMethod()).isEqualTo("POST");
|
|
||||||
assertThat(request.getPath()).isEqualTo("/greeting");
|
|
||||||
assertThat(request.getHeaders().get("testHeaderName")).isEqualTo("testHeader");
|
|
||||||
assertThat(request.getBody().readUtf8()).isEqualTo("testBody");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void formData() throws Exception {
|
|
||||||
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
|
|
||||||
map.add("param1", "value 1");
|
|
||||||
map.add("param2", "value 2");
|
|
||||||
|
|
||||||
service.postForm(map);
|
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
|
||||||
assertThat(request.getHeaders().get("Content-Type"))
|
|
||||||
.isEqualTo("application/x-www-form-urlencoded;charset=UTF-8");
|
|
||||||
assertThat(request.getBody().readUtf8()).isEqualTo("param1=value+1¶m2=value+2");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // gh-30342
|
|
||||||
void multipart() throws InterruptedException {
|
|
||||||
String fileName = "testFileName";
|
|
||||||
String originalFileName = "originalTestFileName";
|
|
||||||
MultipartFile file = new MockMultipartFile(fileName, originalFileName, MediaType.APPLICATION_JSON_VALUE,
|
|
||||||
"test".getBytes());
|
|
||||||
|
|
||||||
service.postMultipart(file, "test2");
|
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
|
||||||
assertThat(request.getHeaders().get("Content-Type")).startsWith("multipart/form-data;boundary=");
|
|
||||||
assertThat(request.getBody().readUtf8()).containsSubsequence(
|
|
||||||
"Content-Disposition: form-data; name=\"file\"; filename=\"originalTestFileName\"",
|
|
||||||
"Content-Type: application/json", "Content-Length: 4", "test",
|
|
||||||
"Content-Disposition: form-data; name=\"anotherPart\"", "Content-Type: text/plain;charset=UTF-8",
|
|
||||||
"Content-Length: 5", "test2");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void putWithCookies() throws InterruptedException {
|
|
||||||
service.putWithCookies("test1", "test2");
|
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
|
||||||
assertThat(request.getMethod()).isEqualTo("PUT");
|
|
||||||
assertThat(request.getHeader("Cookie")).isEqualTo("firstCookie=test1; secondCookie=test2");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void putWithSameNameCookies() throws InterruptedException {
|
|
||||||
service.putWithSameNameCookies("test1", "test2");
|
|
||||||
|
|
||||||
RecordedRequest request = this.server.takeRequest();
|
|
||||||
assertThat(request.getMethod()).isEqualTo("PUT");
|
|
||||||
assertThat(request.getHeader("Cookie")).isEqualTo("testCookie=test1; testCookie=test2");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareResponse() {
|
|
||||||
MockResponse response = new MockResponse();
|
|
||||||
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!");
|
|
||||||
this.server.enqueue(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private interface Service {
|
|
||||||
|
|
||||||
@GetExchange("/greeting")
|
|
||||||
String getGreeting();
|
|
||||||
|
|
||||||
@GetExchange("/greeting/{id}")
|
|
||||||
ResponseEntity<String> getGreetingById(@PathVariable String id);
|
|
||||||
|
|
||||||
@GetExchange("/greeting/{id}")
|
|
||||||
Optional<String> 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<String, String> params);
|
|
||||||
|
|
||||||
@PostExchange
|
|
||||||
void postMultipart(MultipartFile file, @RequestPart String anotherPart);
|
|
||||||
|
|
||||||
@PutExchange
|
|
||||||
void putWithCookies(
|
|
||||||
@CookieValue String firstCookie, @CookieValue String secondCookie);
|
|
||||||
|
|
||||||
@PutExchange
|
|
||||||
void putWithSameNameCookies(
|
|
||||||
@CookieValue("testCookie") String firstCookie, @CookieValue("testCookie") String secondCookie);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue