Browse Source

Polishing contribution

See gh-33697
pull/33778/head
rstoyanchev 1 year ago
parent
commit
73e5aa38ec
  1. 47
      spring-web/src/main/java/org/springframework/web/client/DefaultRestClient.java
  2. 42
      spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java
  3. 2
      spring-web/src/test/java/org/springframework/web/client/RestClientBuilderTests.java
  4. 66
      spring-web/src/test/java/org/springframework/web/client/RestClientIntegrationTests.java
  5. 32
      spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java

47
spring-web/src/main/java/org/springframework/web/client/DefaultRestClient.java

@ -40,7 +40,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest; import org.springframework.http.HttpRequest;
@ -301,8 +300,6 @@ final class DefaultRestClient implements RestClient {
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec { private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
private static final String COOKIE_DELIMITER = "; ";
private final HttpMethod httpMethod; private final HttpMethod httpMethod;
@Nullable @Nullable
@ -557,10 +554,9 @@ final class DefaultRestClient implements RestClient {
try { try {
uri = initUri(); uri = initUri();
HttpHeaders headers = initHeaders(); HttpHeaders headers = initHeaders();
MultiValueMap<String, String> cookies = initCookies(); MultiValueMap<String, String> cookies = initCookies();
if (!CollectionUtils.isEmpty(cookies)) { if (!CollectionUtils.isEmpty(cookies)) {
headers.put(HttpHeaders.COOKIE, List.of(cookiesToHeaderValue(cookies))); headers.set(HttpHeaders.COOKIE, serializeCookies(cookies));
} }
ClientHttpRequest clientRequest = createRequest(uri); ClientHttpRequest clientRequest = createRequest(uri);
@ -638,25 +634,36 @@ final class DefaultRestClient implements RestClient {
} }
private MultiValueMap<String, String> initCookies() { private MultiValueMap<String, String> initCookies() {
MultiValueMap<String, String> mergedCookies = new LinkedMultiValueMap<>(); MultiValueMap<String, String> defaultCookies = DefaultRestClient.this.defaultCookies;
if (CollectionUtils.isEmpty(this.cookies)) {
if(!CollectionUtils.isEmpty(defaultCookies)) { return (defaultCookies != null ? defaultCookies : new LinkedMultiValueMap<>());
mergedCookies.putAll(defaultCookies);
} }
else if (CollectionUtils.isEmpty(defaultCookies)) {
if(!CollectionUtils.isEmpty(this.cookies)) { return this.cookies;
mergedCookies.putAll(this.cookies); }
else {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.putAll(DefaultRestClient.this.defaultCookies);
map.putAll(this.cookies);
return map;
} }
return mergedCookies;
} }
private String cookiesToHeaderValue(MultiValueMap<String, String> cookies) { private String serializeCookies(MultiValueMap<String, String> cookies) {
List<String> flatCookies = new ArrayList<>(); boolean first = true;
cookies.forEach((name, cookieValues) -> cookieValues.forEach(value -> StringBuilder sb = new StringBuilder();
flatCookies.add(new HttpCookie(name, value).toString()) for (Map.Entry<String, List<String>> entry : cookies.entrySet()) {
)); for (String value : entry.getValue()) {
return String.join(COOKIE_DELIMITER, flatCookies); if (!first) {
sb.append("; ");
}
else {
first = false;
}
sb.append(entry.getKey()).append("=").append(value);
}
}
return sb.toString();
} }
private ClientHttpRequest createRequest(URI uri) throws IOException { private ClientHttpRequest createRequest(URI uri) throws IOException {

42
spring-web/src/main/java/org/springframework/web/client/DefaultRestClientBuilder.java

@ -468,21 +468,21 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
public RestClient build() { public RestClient build() {
ClientHttpRequestFactory requestFactory = initRequestFactory(); ClientHttpRequestFactory requestFactory = initRequestFactory();
UriBuilderFactory uriBuilderFactory = initUriBuilderFactory(); UriBuilderFactory uriBuilderFactory = initUriBuilderFactory();
HttpHeaders defaultHeaders = copyDefaultHeaders(); HttpHeaders defaultHeaders = copyDefaultHeaders();
MultiValueMap<String, String> defaultCookies = copyDefaultCookies(); MultiValueMap<String, String> defaultCookies = copyDefaultCookies();
List<HttpMessageConverter<?>> messageConverters = (this.messageConverters != null ?
this.messageConverters : initMessageConverters()); List<HttpMessageConverter<?>> converters =
return new DefaultRestClient(requestFactory, (this.messageConverters != null ? this.messageConverters : initMessageConverters());
this.interceptors, this.initializers, uriBuilderFactory,
defaultHeaders, return new DefaultRestClient(
defaultCookies, requestFactory, this.interceptors, this.initializers,
uriBuilderFactory, defaultHeaders, defaultCookies,
this.defaultRequest, this.defaultRequest,
this.statusHandlers, this.statusHandlers,
messageConverters, converters,
this.observationRegistry, this.observationRegistry, this.observationConvention,
this.observationConvention, new DefaultRestClientBuilder(this));
new DefaultRestClientBuilder(this)
);
} }
private ClientHttpRequestFactory initRequestFactory() { private ClientHttpRequestFactory initRequestFactory() {
@ -519,26 +519,22 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
@Nullable @Nullable
private HttpHeaders copyDefaultHeaders() { private HttpHeaders copyDefaultHeaders() {
if (this.defaultHeaders != null) { if (this.defaultHeaders == null) {
HttpHeaders copy = new HttpHeaders();
this.defaultHeaders.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
return HttpHeaders.readOnlyHttpHeaders(copy);
}
else {
return null; return null;
} }
HttpHeaders copy = new HttpHeaders();
this.defaultHeaders.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
return HttpHeaders.readOnlyHttpHeaders(copy);
} }
@Nullable @Nullable
private MultiValueMap<String, String> copyDefaultCookies() { private MultiValueMap<String, String> copyDefaultCookies() {
if (this.defaultCookies != null) { if (this.defaultCookies == null) {
MultiValueMap<String, String> copy = new LinkedMultiValueMap<>(this.defaultCookies.size());
this.defaultCookies.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
return CollectionUtils.unmodifiableMultiValueMap(copy);
}
else {
return null; return null;
} }
MultiValueMap<String, String> copy = new LinkedMultiValueMap<>(this.defaultCookies.size());
this.defaultCookies.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
return CollectionUtils.unmodifiableMultiValueMap(copy);
} }
} }

2
spring-web/src/test/java/org/springframework/web/client/RestClientBuilderTests.java

@ -151,7 +151,7 @@ public class RestClientBuilderTests {
} }
@Test @Test
void defaultCookieWithMultipleValuesAddsCookieToDefaultCookiesMapWithAllValues() { void defaultCookieWithMultipleValuesAddsCookieToDefaultCookiesMap() {
RestClient.Builder builder = RestClient.builder(); RestClient.Builder builder = RestClient.builder();
builder.defaultCookie("myCookie", "testValue1", "testValue2"); builder.defaultCookie("myCookie", "testValue1", "testValue2");

66
spring-web/src/test/java/org/springframework/web/client/RestClientIntegrationTests.java

@ -23,7 +23,6 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -56,6 +55,7 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.testfixture.xml.Pojo; import org.springframework.web.testfixture.xml.Pojo;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeFalse;
@ -660,8 +660,8 @@ class RestClientIntegrationTests {
startServer(requestFactory); startServer(requestFactory);
String content = "Internal Server error"; String content = "Internal Server error";
prepareResponse(response -> response.setResponseCode(500) prepareResponse(response ->
.setHeader("Content-Type", "text/plain").setBody(content)); response.setResponseCode(500).setHeader("Content-Type", "text/plain").setBody(content));
ResponseEntity<String> result = this.restClient.get() ResponseEntity<String> result = this.restClient.get()
.uri("/").accept(MediaType.APPLICATION_JSON) .uri("/").accept(MediaType.APPLICATION_JSON)
@ -689,7 +689,7 @@ class RestClientIntegrationTests {
String result = this.restClient.get() String result = this.restClient.get()
.uri("/greeting") .uri("/greeting")
.header("X-Test-Header", "testvalue") .header("X-Test-Header", "testvalue")
.exchange((request, response) -> new String(RestClientUtils.getBody(response), StandardCharsets.UTF_8)); .exchange((request, response) -> new String(RestClientUtils.getBody(response), UTF_8));
assertThat(result).isEqualTo("Hello Spring!"); assertThat(result).isEqualTo("Hello Spring!");
@ -753,12 +753,12 @@ class RestClientIntegrationTests {
void exchangeFor404(ClientHttpRequestFactory requestFactory) { void exchangeFor404(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setResponseCode(404) prepareResponse(response ->
.setHeader("Content-Type", "text/plain").setBody("Not Found")); response.setResponseCode(404).setHeader("Content-Type", "text/plain").setBody("Not Found"));
String result = this.restClient.get() String result = this.restClient.get()
.uri("/greeting") .uri("/greeting")
.exchange((request, response) -> new String(RestClientUtils.getBody(response), StandardCharsets.UTF_8)); .exchange((request, response) -> new String(RestClientUtils.getBody(response), UTF_8));
assertThat(result).isEqualTo("Not Found"); assertThat(result).isEqualTo("Not Found");
@ -770,8 +770,8 @@ class RestClientIntegrationTests {
void requestInitializer(ClientHttpRequestFactory requestFactory) { void requestInitializer(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain") prepareResponse(response ->
.setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
RestClient initializedClient = this.restClient.mutate() RestClient initializedClient = this.restClient.mutate()
.requestInitializer(request -> request.getHeaders().add("foo", "bar")) .requestInitializer(request -> request.getHeaders().add("foo", "bar"))
@ -792,9 +792,8 @@ class RestClientIntegrationTests {
void requestInterceptor(ClientHttpRequestFactory requestFactory) { void requestInterceptor(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain") prepareResponse(response ->
.setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
RestClient interceptedClient = this.restClient.mutate() RestClient interceptedClient = this.restClient.mutate()
.requestInterceptor((request, body, execution) -> { .requestInterceptor((request, body, execution) -> {
@ -819,6 +818,7 @@ class RestClientIntegrationTests {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> prepareResponse(response ->
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
RestClient restClientWithCookies = this.restClient.mutate() RestClient restClientWithCookies = this.restClient.mutate()
.defaultCookie("testCookie", "firstValue", "secondValue") .defaultCookie("testCookie", "firstValue", "secondValue")
.build(); .build();
@ -852,8 +852,8 @@ class RestClientIntegrationTests {
RestClient interceptedClient = this.restClient.mutate().requestInterceptor(interceptor).build(); RestClient interceptedClient = this.restClient.mutate().requestInterceptor(interceptor).build();
// header not present // header not present
prepareResponse(response -> response prepareResponse(response ->
.setHeader("Content-Type", "text/plain").setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
assertThatExceptionOfType(MyException.class).isThrownBy(() -> assertThatExceptionOfType(MyException.class).isThrownBy(() ->
interceptedClient.get() interceptedClient.get()
@ -881,8 +881,8 @@ class RestClientIntegrationTests {
void defaultHeaders(ClientHttpRequestFactory requestFactory) { void defaultHeaders(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain") prepareResponse(response ->
.setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
RestClient headersClient = this.restClient.mutate() RestClient headersClient = this.restClient.mutate()
.defaultHeaders(headers -> headers.add("foo", "bar")) .defaultHeaders(headers -> headers.add("foo", "bar"))
@ -903,8 +903,8 @@ class RestClientIntegrationTests {
void defaultRequest(ClientHttpRequestFactory requestFactory) { void defaultRequest(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain") prepareResponse(response ->
.setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
RestClient headersClient = this.restClient.mutate() RestClient headersClient = this.restClient.mutate()
.defaultRequest(request -> request.header("foo", "bar")) .defaultRequest(request -> request.header("foo", "bar"))
@ -925,8 +925,8 @@ class RestClientIntegrationTests {
void defaultRequestOverride(ClientHttpRequestFactory requestFactory) { void defaultRequestOverride(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain") prepareResponse(response ->
.setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
RestClient headersClient = this.restClient.mutate() RestClient headersClient = this.restClient.mutate()
.defaultRequest(request -> request.accept(MediaType.APPLICATION_JSON)) .defaultRequest(request -> request.accept(MediaType.APPLICATION_JSON))
@ -948,8 +948,8 @@ class RestClientIntegrationTests {
void relativeUri(ClientHttpRequestFactory requestFactory) throws URISyntaxException { void relativeUri(ClientHttpRequestFactory requestFactory) throws URISyntaxException {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain") prepareResponse(response ->
.setBody("Hello Spring!")); response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
URI uri = new URI(null, null, "/foo bar", null); URI uri = new URI(null, null, "/foo bar", null);
@ -969,23 +969,28 @@ class RestClientIntegrationTests {
@ParameterizedRestClientTest @ParameterizedRestClientTest
void cookieAddsCookie(ClientHttpRequestFactory requestFactory) { void cookieAddsCookie(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain")
.setBody("Hello Spring!")); prepareResponse(response ->
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
this.restClient.get() this.restClient.get()
.uri("/greeting") .uri("/greeting")
.cookie("foo", "bar") .cookie("c1", "v1a")
.cookie("c1", "v1b")
.cookie("c2", "v2a")
.retrieve() .retrieve()
.body(String.class); .body(String.class);
expectRequest(request -> assertThat(request.getHeader("Cookie")).isEqualTo("foo=bar")); expectRequest(request -> assertThat(request.getHeader("Cookie")).isEqualTo("c1=v1a; c1=v1b; c2=v2a"));
} }
@ParameterizedRestClientTest @ParameterizedRestClientTest
void cookieOverridesDefaultCookie(ClientHttpRequestFactory requestFactory) { void cookieOverridesDefaultCookie(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain")
.setBody("Hello Spring!")); prepareResponse(response ->
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
RestClient restClientWithCookies = this.restClient.mutate() RestClient restClientWithCookies = this.restClient.mutate()
.defaultCookie("testCookie", "firstValue", "secondValue") .defaultCookie("testCookie", "firstValue", "secondValue")
.build(); .build();
@ -1002,8 +1007,9 @@ class RestClientIntegrationTests {
@ParameterizedRestClientTest @ParameterizedRestClientTest
void cookiesCanRemoveCookie(ClientHttpRequestFactory requestFactory) { void cookiesCanRemoveCookie(ClientHttpRequestFactory requestFactory) {
startServer(requestFactory); startServer(requestFactory);
prepareResponse(response -> response.setHeader("Content-Type", "text/plain")
.setBody("Hello Spring!")); prepareResponse(response ->
response.setHeader("Content-Type", "text/plain").setBody("Hello Spring!"));
this.restClient.get() this.restClient.get()
.uri("/greeting") .uri("/greeting")

32
spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java

@ -319,18 +319,14 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
.orElse(null) : null); .orElse(null) : null);
HttpHeaders defaultHeaders = copyDefaultHeaders(); HttpHeaders defaultHeaders = copyDefaultHeaders();
MultiValueMap<String, String> defaultCookies = copyDefaultCookies(); MultiValueMap<String, String> defaultCookies = copyDefaultCookies();
return new DefaultWebClient(exchange, return new DefaultWebClient(
filterFunctions, exchange, filterFunctions,
initUriBuilderFactory(), initUriBuilderFactory(), defaultHeaders, defaultCookies,
defaultHeaders,
defaultCookies,
this.defaultRequest, this.defaultRequest,
this.statusHandlers, this.statusHandlers,
this.observationRegistry, this.observationRegistry, this.observationConvention,
this.observationConvention,
new DefaultWebClientBuilder(this)); new DefaultWebClientBuilder(this));
} }
@ -374,26 +370,22 @@ final class DefaultWebClientBuilder implements WebClient.Builder {
@Nullable @Nullable
private HttpHeaders copyDefaultHeaders() { private HttpHeaders copyDefaultHeaders() {
if (this.defaultHeaders != null) { if (this.defaultHeaders == null) {
HttpHeaders copy = new HttpHeaders();
this.defaultHeaders.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
return HttpHeaders.readOnlyHttpHeaders(copy);
}
else {
return null; return null;
} }
HttpHeaders headers = new HttpHeaders();
this.defaultHeaders.forEach((key, values) -> headers.put(key, new ArrayList<>(values)));
return HttpHeaders.readOnlyHttpHeaders(headers);
} }
@Nullable @Nullable
private MultiValueMap<String, String> copyDefaultCookies() { private MultiValueMap<String, String> copyDefaultCookies() {
if (this.defaultCookies != null) { if (this.defaultCookies == null) {
MultiValueMap<String, String> copy = new LinkedMultiValueMap<>(this.defaultCookies.size());
this.defaultCookies.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
return CollectionUtils.unmodifiableMultiValueMap(copy);
}
else {
return null; return null;
} }
MultiValueMap<String, String> map = new LinkedMultiValueMap<>(this.defaultCookies.size());
this.defaultCookies.forEach((key, values) -> map.put(key, new ArrayList<>(values)));
return CollectionUtils.unmodifiableMultiValueMap(map);
} }
} }

Loading…
Cancel
Save