Skip to content

Commit aad21d1

Browse files
committed
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
1 parent 43b1a66 commit aad21d1

File tree

14 files changed

+517
-678
lines changed

14 files changed

+517
-678
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package org.springframework.boot.autoconfigure.web.client;
1818

19+
import java.util.Collection;
1920
import java.util.List;
21+
import java.util.function.BiFunction;
2022
import java.util.stream.Collectors;
2123

2224
import org.springframework.beans.factory.ObjectProvider;
@@ -32,6 +34,7 @@
3234
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition;
3335
import org.springframework.boot.web.client.RestTemplateBuilder;
3436
import org.springframework.boot.web.client.RestTemplateCustomizer;
37+
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
3538
import org.springframework.context.annotation.Bean;
3639
import org.springframework.context.annotation.Conditional;
3740
import org.springframework.context.annotation.Configuration;
@@ -53,15 +56,23 @@ public class RestTemplateAutoConfiguration {
5356
@Bean
5457
@ConditionalOnMissingBean
5558
public RestTemplateBuilder restTemplateBuilder(ObjectProvider<HttpMessageConverters> messageConverters,
56-
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers) {
59+
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers,
60+
ObjectProvider<RestTemplateRequestCustomizer<?>> restTemplateRequestCustomizers) {
5761
RestTemplateBuilder builder = new RestTemplateBuilder();
5862
HttpMessageConverters converters = messageConverters.getIfUnique();
5963
if (converters != null) {
6064
builder = builder.messageConverters(converters.getConverters());
6165
}
62-
List<RestTemplateCustomizer> customizers = restTemplateCustomizers.orderedStream().collect(Collectors.toList());
66+
builder = addCustomizers(builder, restTemplateCustomizers, RestTemplateBuilder::customizers);
67+
builder = addCustomizers(builder, restTemplateRequestCustomizers, RestTemplateBuilder::requestCustomizers);
68+
return builder;
69+
}
70+
71+
private <T> RestTemplateBuilder addCustomizers(RestTemplateBuilder builder, ObjectProvider<T> objectProvider,
72+
BiFunction<RestTemplateBuilder, Collection<T>, RestTemplateBuilder> method) {
73+
List<T> customizers = objectProvider.orderedStream().collect(Collectors.toList());
6374
if (!customizers.isEmpty()) {
64-
builder = builder.customizers(customizers);
75+
return method.apply(builder, customizers);
6576
}
6677
return builder;
6778
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfigurationTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.autoconfigure.web.client;
1818

19+
import java.util.Collections;
1920
import java.util.List;
2021

2122
import org.junit.jupiter.api.Test;
@@ -28,14 +29,21 @@
2829
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
2930
import org.springframework.boot.web.client.RestTemplateBuilder;
3031
import org.springframework.boot.web.client.RestTemplateCustomizer;
32+
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
3133
import org.springframework.context.annotation.Bean;
3234
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.http.HttpStatus;
36+
import org.springframework.http.client.ClientHttpRequestFactory;
3337
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
3438
import org.springframework.http.converter.HttpMessageConverter;
3539
import org.springframework.http.converter.StringHttpMessageConverter;
40+
import org.springframework.mock.http.client.MockClientHttpRequest;
41+
import org.springframework.mock.http.client.MockClientHttpResponse;
3642
import org.springframework.web.client.RestTemplate;
3743

3844
import static org.assertj.core.api.Assertions.assertThat;
45+
import static org.mockito.ArgumentMatchers.any;
46+
import static org.mockito.BDDMockito.given;
3947
import static org.mockito.Mockito.mock;
4048
import static org.mockito.Mockito.verify;
4149

@@ -109,6 +117,20 @@ void restTemplateShouldApplyCustomizer() {
109117
});
110118
}
111119

120+
@Test
121+
void restTemplateShouldApplyRequestCustomizer() {
122+
this.contextRunner.withUserConfiguration(RestTemplateRequestCustomizerConfig.class).run((context) -> {
123+
RestTemplateBuilder builder = context.getBean(RestTemplateBuilder.class);
124+
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
125+
MockClientHttpRequest request = new MockClientHttpRequest();
126+
request.setResponse(new MockClientHttpResponse(new byte[0], HttpStatus.OK));
127+
given(requestFactory.createRequest(any(), any())).willReturn(request);
128+
RestTemplate restTemplate = builder.requestFactory(() -> requestFactory).build();
129+
restTemplate.getForEntity("http://localhost:8080/test", String.class);
130+
assertThat(request.getHeaders()).containsEntry("spring", Collections.singletonList("boot"));
131+
});
132+
}
133+
112134
@Test
113135
void builderShouldBeFreshForEachUse() {
114136
this.contextRunner.withUserConfiguration(DirtyRestTemplateConfig.class)
@@ -189,6 +211,16 @@ public RestTemplateCustomizer restTemplateCustomizer() {
189211

190212
}
191213

214+
@Configuration(proxyBeanMethods = false)
215+
static class RestTemplateRequestCustomizerConfig {
216+
217+
@Bean
218+
public RestTemplateRequestCustomizer<?> restTemplateRequestCustomizer() {
219+
return (request) -> request.getHeaders().add("spring", "boot");
220+
}
221+
222+
}
223+
192224
static class CustomHttpMessageConverter extends StringHttpMessageConverter {
193225

194226
}

spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/client/TestRestTemplateTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ void useTheSameRequestFactoryClassWithBasicAuth() {
100100
TestRestTemplate testRestTemplate = new TestRestTemplate(builder).withBasicAuth("test", "test");
101101
RestTemplate restTemplate = testRestTemplate.getRestTemplate();
102102
assertThat(restTemplate.getRequestFactory().getClass().getName())
103-
.contains("HttpHeadersCustomizingClientHttpRequestFactory");
103+
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
104104
Object requestFactory = ReflectionTestUtils.getField(restTemplate.getRequestFactory(), "requestFactory");
105105
assertThat(requestFactory).isEqualTo(customFactory).hasSameClassAs(customFactory);
106106
}
@@ -208,7 +208,7 @@ void withBasicAuthAddsBasicAuthClientFactoryWhenNotAlreadyPresent() throws Excep
208208
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
209209
assertThat(getConverterClasses(original)).containsExactlyElementsOf(getConverterClasses(basicAuth));
210210
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName())
211-
.contains("HttpHeadersCustomizingClientHttpRequestFactory");
211+
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
212212
assertThat(ReflectionTestUtils.getField(basicAuth.getRestTemplate().getRequestFactory(), "requestFactory"))
213213
.isInstanceOf(CustomHttpComponentsClientHttpRequestFactory.class);
214214
assertThat(basicAuth.getRestTemplate().getInterceptors()).isEmpty();
@@ -221,7 +221,7 @@ void withBasicAuthReplacesBasicAuthClientFactoryWhenAlreadyPresent() throws Exce
221221
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
222222
assertThat(getConverterClasses(basicAuth)).containsExactlyElementsOf(getConverterClasses(original));
223223
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName())
224-
.contains("HttpHeadersCustomizingClientHttpRequestFactory");
224+
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
225225
assertThat(ReflectionTestUtils.getField(basicAuth.getRestTemplate().getRequestFactory(), "requestFactory"))
226226
.isInstanceOf(CustomHttpComponentsClientHttpRequestFactory.class);
227227
assertThat(basicAuth.getRestTemplate().getInterceptors()).isEmpty();

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/AbstractHttpHeadersDefaultingCustomizer.java

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,34 +22,31 @@
2222
import org.springframework.util.Assert;
2323

2424
/**
25-
* {@link AbstractHttpHeadersDefaultingCustomizer} that applies basic authentication
26-
* header unless it was provided in the request.
25+
* Basic authentication details to be applied to {@link HttpHeaders}.
2726
*
2827
* @author Dmytro Nosan
2928
* @author Ilya Lukyanovich
30-
* @see HttpHeadersCustomizingClientHttpRequestFactory
3129
*/
32-
class BasicAuthenticationHeaderDefaultingCustomizer extends AbstractHttpHeadersDefaultingCustomizer {
30+
class BasicAuthentication {
3331

3432
private final String username;
3533

3634
private final String password;
3735

3836
private final Charset charset;
3937

40-
BasicAuthenticationHeaderDefaultingCustomizer(String username, String password, Charset charset) {
38+
BasicAuthentication(String username, String password, Charset charset) {
4139
Assert.notNull(username, "Username must not be null");
4240
Assert.notNull(password, "Password must not be null");
4341
this.username = username;
4442
this.password = password;
4543
this.charset = charset;
4644
}
4745

48-
@Override
49-
protected HttpHeaders createHeaders() {
50-
HttpHeaders headers = new HttpHeaders();
51-
headers.setBasicAuth(this.username, this.password, this.charset);
52-
return headers;
46+
public void applyTo(HttpHeaders headers) {
47+
if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
48+
headers.setBasicAuth(this.username, this.password, this.charset);
49+
}
5350
}
5451

5552
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/HttpHeadersCustomizingClientHttpRequestFactory.java

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)