Commit aad21d19 authored by Phillip Webb's avatar Phillip Webb

Polish "Support default headers with RestTemplateBuilder"

Broaden the scope of customizer support so that instead of focusing
just on headers, we can now customize any outgoing `HttpClientRequest`.
Also update auto-configuration to automatically add any
`RestTemplateRequestCustomizer` beans to the builder.

See gh-17091
parent 43b1a667
......@@ -16,7 +16,9 @@
package org.springframework.boot.autoconfigure.web.client;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
......@@ -32,6 +34,7 @@ import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConf
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
......@@ -53,15 +56,23 @@ public class RestTemplateAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RestTemplateBuilder restTemplateBuilder(ObjectProvider<HttpMessageConverters> messageConverters,
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers) {
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
RestTemplateBuilder builder = new RestTemplateBuilder();
HttpMessageConverters converters = messageConverters.getIfUnique();
if (converters != null) {
builder = builder.messageConverters(converters.getConverters());
}
List<RestTemplateCustomizer> customizers = restTemplateCustomizers.orderedStream().collect(Collectors.toList());
builder = addCustomizers(builder, restTemplateCustomizers, RestTemplateBuilder::customizers);
builder = addCustomizers(builder, restTemplateRequestCustomizers, RestTemplateBuilder::requestCustomizers);
return builder;
}
private <T> RestTemplateBuilder addCustomizers(RestTemplateBuilder builder, ObjectProvider<T> objectProvider,
BiFunction<RestTemplateBuilder, Collection<T>, RestTemplateBuilder> method) {
List<T> customizers = objectProvider.orderedStream().collect(Collectors.toList());
if (!customizers.isEmpty()) {
builder = builder.customizers(customizers);
return method.apply(builder, customizers);
}
return builder;
}
......
......@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.web.client;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.Test;
......@@ -28,14 +29,21 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
......@@ -109,6 +117,20 @@ class RestTemplateAutoConfigurationTests {
});
}
@Test
void restTemplateShouldApplyRequestCustomizer() {
this.contextRunner.withUserConfiguration(RestTemplateRequestCustomizerConfig.class).run((context) -> {
RestTemplateBuilder builder = context.getBean(RestTemplateBuilder.class);
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
MockClientHttpRequest request = new MockClientHttpRequest();
request.setResponse(new MockClientHttpResponse(new byte[0], HttpStatus.OK));
given(requestFactory.createRequest(any(), any())).willReturn(request);
RestTemplate restTemplate = builder.requestFactory(() -> requestFactory).build();
restTemplate.getForEntity("http://localhost:8080/test", String.class);
assertThat(request.getHeaders()).containsEntry("spring", Collections.singletonList("boot"));
});
}
@Test
void builderShouldBeFreshForEachUse() {
this.contextRunner.withUserConfiguration(DirtyRestTemplateConfig.class)
......@@ -189,6 +211,16 @@ class RestTemplateAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class RestTemplateRequestCustomizerConfig {
@Bean
public RestTemplateRequestCustomizer<?> restTemplateRequestCustomizer() {
return (request) -> request.getHeaders().add("spring", "boot");
}
}
static class CustomHttpMessageConverter extends StringHttpMessageConverter {
}
......
......@@ -100,7 +100,7 @@ class TestRestTemplateTests {
TestRestTemplate testRestTemplate = new TestRestTemplate(builder).withBasicAuth("test", "test");
RestTemplate restTemplate = testRestTemplate.getRestTemplate();
assertThat(restTemplate.getRequestFactory().getClass().getName())
.contains("HttpHeadersCustomizingClientHttpRequestFactory");
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
Object requestFactory = ReflectionTestUtils.getField(restTemplate.getRequestFactory(), "requestFactory");
assertThat(requestFactory).isEqualTo(customFactory).hasSameClassAs(customFactory);
}
......@@ -208,7 +208,7 @@ class TestRestTemplateTests {
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
assertThat(getConverterClasses(original)).containsExactlyElementsOf(getConverterClasses(basicAuth));
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName())
.contains("HttpHeadersCustomizingClientHttpRequestFactory");
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
assertThat(ReflectionTestUtils.getField(basicAuth.getRestTemplate().getRequestFactory(), "requestFactory"))
.isInstanceOf(CustomHttpComponentsClientHttpRequestFactory.class);
assertThat(basicAuth.getRestTemplate().getInterceptors()).isEmpty();
......@@ -221,7 +221,7 @@ class TestRestTemplateTests {
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
assertThat(getConverterClasses(basicAuth)).containsExactlyElementsOf(getConverterClasses(original));
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName())
.contains("HttpHeadersCustomizingClientHttpRequestFactory");
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
assertThat(ReflectionTestUtils.getField(basicAuth.getRestTemplate().getRequestFactory(), "requestFactory"))
.isInstanceOf(CustomHttpComponentsClientHttpRequestFactory.class);
assertThat(basicAuth.getRestTemplate().getInterceptors()).isEmpty();
......
/*
* Copyright 2012-2019 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.boot.web.client;
import org.springframework.http.HttpHeaders;
/**
* {@link HttpHeadersCustomizer} that only adds headers that were not populated in the
* request.
*
* @author Ilya Lukyanovich
*/
public abstract class AbstractHttpHeadersDefaultingCustomizer implements HttpHeadersCustomizer {
@Override
public void applyTo(HttpHeaders headers) {
createHeaders().forEach((key, value) -> headers.merge(key, value, (oldValue, ignored) -> oldValue));
}
protected abstract HttpHeaders createHeaders();
}
......@@ -22,14 +22,12 @@ import org.springframework.http.HttpHeaders;
import org.springframework.util.Assert;
/**
* {@link AbstractHttpHeadersDefaultingCustomizer} that applies basic authentication
* header unless it was provided in the request.
* Basic authentication details to be applied to {@link HttpHeaders}.
*
* @author Dmytro Nosan
* @author Ilya Lukyanovich
* @see HttpHeadersCustomizingClientHttpRequestFactory
*/
class BasicAuthenticationHeaderDefaultingCustomizer extends AbstractHttpHeadersDefaultingCustomizer {
class BasicAuthentication {
private final String username;
......@@ -37,7 +35,7 @@ class BasicAuthenticationHeaderDefaultingCustomizer extends AbstractHttpHeadersD
private final Charset charset;
BasicAuthenticationHeaderDefaultingCustomizer(String username, String password, Charset charset) {
BasicAuthentication(String username, String password, Charset charset) {
Assert.notNull(username, "Username must not be null");
Assert.notNull(password, "Password must not be null");
this.username = username;
......@@ -45,11 +43,10 @@ class BasicAuthenticationHeaderDefaultingCustomizer extends AbstractHttpHeadersD
this.charset = charset;
}
@Override
protected HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(this.username, this.password, this.charset);
return headers;
public void applyTo(HttpHeaders headers) {
if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
headers.setBasicAuth(this.username, this.password, this.charset);
}
}
}
......@@ -18,40 +18,52 @@ package org.springframework.boot.web.client;
import java.io.IOException;
import java.net.URI;
import java.util.Collection;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Set;
import org.springframework.boot.util.LambdaSafe;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;
/**
* {@link ClientHttpRequestFactory} to apply default headers to a request unless header
* values were provided.
* {@link ClientHttpRequestFactory} to apply customizations from the
* {@link RestTemplateBuilder}.
*
* @author Ilya Lukyanovich
* @author Dmytro Nosan
* @author Ilya Lukyanovich
*/
class HttpHeadersCustomizingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
class RestTemplateBuilderClientHttpRequestFactoryWrapper extends AbstractClientHttpRequestFactoryWrapper {
private final BasicAuthentication basicAuthentication;
private final Map<String, String> defaultHeaders;
private final Collection<? extends HttpHeadersCustomizer> customizers;
private final Set<RestTemplateRequestCustomizer<?>> requestCustomizers;
HttpHeadersCustomizingClientHttpRequestFactory(Collection<? extends HttpHeadersCustomizer> customizers,
ClientHttpRequestFactory clientHttpRequestFactory) {
super(clientHttpRequestFactory);
Assert.notEmpty(customizers, "Customizers must not be empty");
this.customizers = customizers;
RestTemplateBuilderClientHttpRequestFactoryWrapper(ClientHttpRequestFactory requestFactory,
BasicAuthentication basicAuthentication, Map<String, String> defaultHeaders,
Set<RestTemplateRequestCustomizer<?>> requestCustomizers) {
super(requestFactory);
this.basicAuthentication = basicAuthentication;
this.defaultHeaders = defaultHeaders;
this.requestCustomizers = requestCustomizers;
}
@NotNull
@Override
protected ClientHttpRequest createRequest(@NotNull URI uri, @NotNull HttpMethod httpMethod,
ClientHttpRequestFactory requestFactory) throws IOException {
@SuppressWarnings("unchecked")
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory)
throws IOException {
ClientHttpRequest request = requestFactory.createRequest(uri, httpMethod);
this.customizers.forEach((customizer) -> customizer.applyTo(request.getHeaders()));
HttpHeaders headers = request.getHeaders();
if (this.basicAuthentication != null) {
this.basicAuthentication.applyTo(headers);
}
this.defaultHeaders.forEach(headers::addIfAbsent);
LambdaSafe.callbacks(RestTemplateRequestCustomizer.class, this.requestCustomizers, request)
.invoke((customizer) -> customizer.customize(request));
return request;
}
......
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2018 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.
......@@ -16,21 +16,26 @@
package org.springframework.boot.web.client;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.web.client.RestTemplate;
/**
* Callback interface that can be used to customize a {@link HttpHeaders}.
* Callback interface that can be used to customize the {@link ClientHttpRequest} sent
* from a {@link RestTemplate}.
*
* @param <T> the {@link ClientHttpRequest} type
* @author Ilya Lukyanovich
* @see HttpHeadersCustomizingClientHttpRequestFactory
* @author Phillip Webb
* @since 2.2.0
* @see RestTemplateBuilder
*/
@FunctionalInterface
public interface HttpHeadersCustomizer {
public interface RestTemplateRequestCustomizer<T extends ClientHttpRequest> {
/**
* Callback to customize a {@link HttpHeaders} instance.
* @param headers the headers to customize
* Customize the specified {@link ClientHttpRequest}.
* @param request the request to customize
*/
void applyTo(HttpHeaders headers);
void customize(T request);
}
/*
* Copyright 2012-2019 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.boot.web.client;
import java.nio.charset.Charset;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
/**
* A {@link AbstractHttpHeadersDefaultingCustomizer} that uses provided
* {@link HttpHeaders} instance as default headers.
*
* @author Ilya Lukyanovich
* @see HttpHeadersCustomizingClientHttpRequestFactory
*/
public class SimpleHttpHeaderDefaultingCustomizer extends AbstractHttpHeadersDefaultingCustomizer {
private final HttpHeaders httpHeaders;
public SimpleHttpHeaderDefaultingCustomizer(HttpHeaders httpHeaders) {
Assert.notNull(httpHeaders, "Header must not be null");
this.httpHeaders = httpHeaders;
}
@Override
protected HttpHeaders createHeaders() {
return new HttpHeaders(new LinkedMultiValueMap<>(this.httpHeaders));
}
/**
* A factory method that creates a {@link SimpleHttpHeaderDefaultingCustomizer} with a
* single header and a single value.
* @param header the header
* @param value the value
* @return new {@link SimpleHttpHeaderDefaultingCustomizer} instance
* @see HttpHeaders#set(String, String)
*/
public static HttpHeadersCustomizer singleHeader(@NonNull String header, @NonNull String value) {
Assert.notNull(header, "Header must not be null empty");
Assert.notNull(value, "Value must not be null empty");
HttpHeaders headers = new HttpHeaders();
headers.set(header, value);
return new SimpleHttpHeaderDefaultingCustomizer(headers);
}
/**
* A factory method that creates a {@link SimpleHttpHeaderDefaultingCustomizer} for
* {@link HttpHeaders#AUTHORIZATION} header with pre-defined username and password
* pair.
* @param username the username
* @param password the password
* @return new {@link SimpleHttpHeaderDefaultingCustomizer} instance
* @see #basicAuthentication(String, String, Charset)
*/
public static HttpHeadersCustomizer basicAuthentication(@NonNull String username, @NonNull String password) {
return basicAuthentication(username, password, null);
}
/**
* A factory method that creates a {@link SimpleHttpHeaderDefaultingCustomizer} for
* {@link HttpHeaders#AUTHORIZATION} header with pre-defined username and password
* pair.
* @param username the username
* @param password the password
* @param charset the header encoding charset
* @return new {@link SimpleHttpHeaderDefaultingCustomizer} instance
* @see HttpHeaders#setBasicAuth(String, String, Charset)
*/
public static HttpHeadersCustomizer basicAuthentication(@NonNull String username, @NonNull String password,
@Nullable Charset charset) {
Assert.notNull(username, "Username must not be null");
Assert.notNull(password, "Password must not be null");
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(username, password, charset);
return new SimpleHttpHeaderDefaultingCustomizer(headers);
}
}
......@@ -18,11 +18,15 @@ package org.springframework.boot.web.client;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
......@@ -32,20 +36,22 @@ import org.springframework.http.client.ClientHttpRequestFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HttpHeadersCustomizingClientHttpRequestFactory}.
* Tests for {@link RestTemplateBuilderClientHttpRequestFactoryWrapper}.
*
* @author Dmytro Nosan
* @author Ilya Lukyanovich
* @author Phillip Webb
*/
public class HttpHeadersCustomizingClientHttpRequestFactoryTests {
private final HttpHeaders headers = new HttpHeaders();
public class RestTemplateBuilderClientHttpRequestFactoryWrapperTests {
private ClientHttpRequestFactory requestFactory;
private final HttpHeaders headers = new HttpHeaders();
@BeforeEach
public void setUp() throws IOException {
this.requestFactory = mock(ClientHttpRequestFactory.class);
......@@ -55,36 +61,51 @@ public class HttpHeadersCustomizingClientHttpRequestFactoryTests {
}
@Test
void shouldAddAuthorizationHeader() throws IOException {
this.requestFactory = new HttpHeadersCustomizingClientHttpRequestFactory(
Collections.singleton(SimpleHttpHeaderDefaultingCustomizer.basicAuthentication("spring", "boot", null)),
this.requestFactory);
void createRequestWhenHasBasicAuthAndNoAuthHeaderAddsHeader() throws IOException {
this.requestFactory = new RestTemplateBuilderClientHttpRequestFactoryWrapper(this.requestFactory,
new BasicAuthentication("spring", "boot", null), Collections.emptyMap(), Collections.emptySet());
ClientHttpRequest request = createRequest();
assertThat(request.getHeaders().get(HttpHeaders.AUTHORIZATION)).containsExactly("Basic c3ByaW5nOmJvb3Q=");
}
@Test
void shouldNotAddAuthorizationHeaderAuthorizationAlreadySet() throws IOException {
void createRequestWhenHasBasicAuthAndExistingAuthHeaderDoesNotAddHeader() throws IOException {
this.headers.setBasicAuth("boot", "spring");
this.requestFactory = new HttpHeadersCustomizingClientHttpRequestFactory(
Collections.singleton(SimpleHttpHeaderDefaultingCustomizer.basicAuthentication("spring", "boot", null)),
this.requestFactory);
this.requestFactory = new RestTemplateBuilderClientHttpRequestFactoryWrapper(this.requestFactory,
new BasicAuthentication("spring", "boot", null), Collections.emptyMap(), Collections.emptySet());
ClientHttpRequest request = createRequest();
assertThat(request.getHeaders().get(HttpHeaders.AUTHORIZATION)).doesNotContain("Basic c3ByaW5nOmJvb3Q=");
}
@Test
void createRequestWhenHasDefaultHeadersAddsMissing() throws IOException {
this.headers.add("one", "existing");
Map<String, String> defaultHeaders = new LinkedHashMap<>();
defaultHeaders.put("one", "1");
defaultHeaders.put("two", "2");
defaultHeaders.put("three", "3");
this.requestFactory = new RestTemplateBuilderClientHttpRequestFactoryWrapper(this.requestFactory, null,
defaultHeaders, Collections.emptySet());
ClientHttpRequest request = createRequest();
assertThat(request.getHeaders().get("one")).containsExactly("existing");
assertThat(request.getHeaders().get("two")).containsExactly("2");
assertThat(request.getHeaders().get("three")).containsExactly("3");
}
@Test
void shouldApplyCustomizersInTheProvidedOrder() throws IOException {
this.requestFactory = new HttpHeadersCustomizingClientHttpRequestFactory(
Arrays.asList((headers) -> headers.add("foo", "bar"),
SimpleHttpHeaderDefaultingCustomizer.basicAuthentication("spring", "boot", null),
SimpleHttpHeaderDefaultingCustomizer.singleHeader(HttpHeaders.AUTHORIZATION, "won't do")),
this.requestFactory);
@SuppressWarnings("unchecked")
void createRequestWhenHasRequestCustomizersAppliesThemInOrder() throws IOException {
Set<RestTemplateRequestCustomizer<?>> customizers = new LinkedHashSet<>();
customizers.add(mock(RestTemplateRequestCustomizer.class));
customizers.add(mock(RestTemplateRequestCustomizer.class));
customizers.add(mock(RestTemplateRequestCustomizer.class));
this.requestFactory = new RestTemplateBuilderClientHttpRequestFactoryWrapper(this.requestFactory, null,
Collections.emptyMap(), customizers);
ClientHttpRequest request = createRequest();
assertThat(request.getHeaders()).containsOnlyKeys("foo", HttpHeaders.AUTHORIZATION);
assertThat(request.getHeaders().get("foo")).containsExactly("bar");
assertThat(request.getHeaders().get(HttpHeaders.AUTHORIZATION)).containsExactly("Basic c3ByaW5nOmJvb3Q=");
InOrder inOrder = inOrder(customizers.toArray());
for (RestTemplateRequestCustomizer<?> customizer : customizers) {
inOrder.verify((RestTemplateRequestCustomizer<ClientHttpRequest>) customizer).customize(request);
}
}
private ClientHttpRequest createRequest() throws IOException {
......
......@@ -16,6 +16,7 @@
package org.springframework.boot.web.client;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
......@@ -52,6 +53,7 @@ import org.springframework.web.util.UriTemplateHandler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.entry;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
......@@ -69,6 +71,7 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
* @author Andy Wilkinson
* @author Dmytro Nosan
* @author Kevin Strijbos
* @author Ilya Lukyanovich
*/
class RestTemplateBuilderTests {
......@@ -310,17 +313,45 @@ class RestTemplateBuilderTests {
assertThat(request.getHeaders().get(HttpHeaders.AUTHORIZATION)).containsExactly("Basic c3ByaW5nOmJvb3Q=");
}
@Test
void defaultHeaderAddsHeader() throws IOException {
RestTemplate template = this.builder.defaultHeader("spring", "boot").build();
ClientHttpRequestFactory requestFactory = template.getRequestFactory();
ClientHttpRequest request = requestFactory.createRequest(URI.create("http://localhost"), HttpMethod.GET);
assertThat(request.getHeaders()).contains(entry("spring", Collections.singletonList("boot")));
}
@Test
void requestCustomizersAddsCustomizers() throws IOException {
RestTemplate template = this.builder
.requestCustomizers((request) -> request.getHeaders().add("spring", "framework")).build();
ClientHttpRequestFactory requestFactory = template.getRequestFactory();
ClientHttpRequest request = requestFactory.createRequest(URI.create("http://localhost"), HttpMethod.GET);
assertThat(request.getHeaders()).contains(entry("spring", Collections.singletonList("framework")));
}
@Test
void additionalRequestCustomizersAddsCustomizers() throws IOException {
RestTemplate template = this.builder
.requestCustomizers((request) -> request.getHeaders().add("spring", "framework"))
.additionalRequestCustomizers((request) -> request.getHeaders().add("for", "java")).build();
ClientHttpRequestFactory requestFactory = template.getRequestFactory();
ClientHttpRequest request = requestFactory.createRequest(URI.create("http://localhost"), HttpMethod.GET);
assertThat(request.getHeaders()).contains(entry("spring", Collections.singletonList("framework")))
.contains(entry("for", Collections.singletonList("java")));
}
@Test
void customizersWhenCustomizersAreNullShouldThrowException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.builder.customizers((RestTemplateCustomizer[]) null))
.withMessageContaining("RestTemplateCustomizers must not be null");
.withMessageContaining("Customizers must not be null");
}
@Test
void customizersCollectionWhenCustomizersAreNullShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.builder.customizers((Set<RestTemplateCustomizer>) null))
.withMessageContaining("RestTemplateCustomizers must not be null");
.withMessageContaining("Customizers must not be null");
}
@Test
......@@ -352,7 +383,7 @@ class RestTemplateBuilderTests {
void additionalCustomizersWhenCustomizersAreNullShouldThrowException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.builder.additionalCustomizers((RestTemplateCustomizer[]) null))
.withMessageContaining("RestTemplateCustomizers must not be null");
.withMessageContaining("Customizers must not be null");
}
@Test
......@@ -387,7 +418,8 @@ class RestTemplateBuilderTests {
assertThat(actualRequestFactory).isInstanceOf(InterceptingClientHttpRequestFactory.class);
ClientHttpRequestFactory authRequestFactory = (ClientHttpRequestFactory) ReflectionTestUtils
.getField(actualRequestFactory, "requestFactory");
assertThat(authRequestFactory).isInstanceOf(HttpHeadersCustomizingClientHttpRequestFactory.class);
assertThat(authRequestFactory)
.isInstanceOf(RestTemplateBuilderClientHttpRequestFactoryWrapper.class);
assertThat(authRequestFactory).hasFieldOrPropertyWithValue("requestFactory", requestFactory);
}).build();
}
......
/*
* Copyright 2012-2019 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.boot.web.client;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SimpleHttpHeaderDefaultingCustomizer}.
*
* @author Ilya Lukyanovich
*/
class SimpleHttpHeaderDefaultingCustomizerTest {
@Test
void testApplyTo_shouldAddAllHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("foo", "bar");
httpHeaders.add("donut", "42");
SimpleHttpHeaderDefaultingCustomizer customizer = new SimpleHttpHeaderDefaultingCustomizer(httpHeaders);
HttpHeaders provided = new HttpHeaders();
customizer.applyTo(provided);
assertThat(provided).containsOnlyKeys("foo", "donut");
assertThat(provided.get("foo")).containsExactly("bar");
assertThat(provided.get("donut")).containsExactly("42");
}
@Test
void testApplyTo_shouldIgnoreProvided() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("foo", "bar");
httpHeaders.add("donut", "42");
SimpleHttpHeaderDefaultingCustomizer customizer = new SimpleHttpHeaderDefaultingCustomizer(httpHeaders);
HttpHeaders provided = new HttpHeaders();
provided.add("donut", "touchme");
customizer.applyTo(provided);
assertThat(provided).containsOnlyKeys("foo", "donut");
assertThat(provided.get("foo")).containsExactly("bar");
assertThat(provided.get("donut")).containsExactly("touchme");
}
@Test
void testSingleHeader() {
HttpHeaders provided = new HttpHeaders();
SimpleHttpHeaderDefaultingCustomizer.singleHeader("foo", "bar").applyTo(provided);
assertThat(provided).containsOnlyKeys("foo");
assertThat(provided.get("foo")).containsExactly("bar");
}
@Test
void testBasicAuthentication() {
HttpHeaders provided = new HttpHeaders();
SimpleHttpHeaderDefaultingCustomizer.basicAuthentication("spring", "boot").applyTo(provided);
assertThat(provided).containsOnlyKeys(HttpHeaders.AUTHORIZATION);
assertThat(provided.get(HttpHeaders.AUTHORIZATION)).containsExactly("bar");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment