Skip to content

Commit 2888dde

Browse files
committed
Merge pull request #17091 from L00kian
* pr/17091: Polish "Support default headers with RestTemplateBuilder" Support default headers with RestTemplateBuilder Closes gh-17091
2 parents 9b5cb4f + aad21d1 commit 2888dde

File tree

11 files changed

+575
-288
lines changed

11 files changed

+575
-288
lines changed

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

+14-3
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

+32
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

+18-25
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.boot.web.client.RestTemplateBuilder;
3232
import org.springframework.core.ParameterizedTypeReference;
3333
import org.springframework.http.HttpEntity;
34+
import org.springframework.http.HttpHeaders;
3435
import org.springframework.http.HttpMethod;
3536
import org.springframework.http.HttpStatus;
3637
import org.springframework.http.RequestEntity;
@@ -43,6 +44,7 @@
4344
import org.springframework.mock.http.client.MockClientHttpRequest;
4445
import org.springframework.mock.http.client.MockClientHttpResponse;
4546
import org.springframework.test.util.ReflectionTestUtils;
47+
import org.springframework.util.Base64Utils;
4648
import org.springframework.util.ReflectionUtils;
4749
import org.springframework.util.ReflectionUtils.MethodCallback;
4850
import org.springframework.web.client.ResponseErrorHandler;
@@ -97,7 +99,8 @@ void useTheSameRequestFactoryClassWithBasicAuth() {
9799
RestTemplateBuilder builder = new RestTemplateBuilder().requestFactory(() -> customFactory);
98100
TestRestTemplate testRestTemplate = new TestRestTemplate(builder).withBasicAuth("test", "test");
99101
RestTemplate restTemplate = testRestTemplate.getRestTemplate();
100-
assertThat(restTemplate.getRequestFactory().getClass().getName()).contains("BasicAuth");
102+
assertThat(restTemplate.getRequestFactory().getClass().getName())
103+
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
101104
Object requestFactory = ReflectionTestUtils.getField(restTemplate.getRequestFactory(), "requestFactory");
102105
assertThat(requestFactory).isEqualTo(customFactory).hasSameClassAs(customFactory);
103106
}
@@ -125,10 +128,9 @@ void getRootUriRootUriNotSet() {
125128
}
126129

127130
@Test
128-
void authenticated() {
129-
RestTemplate restTemplate = new TestRestTemplate("user", "password").getRestTemplate();
130-
ClientHttpRequestFactory factory = restTemplate.getRequestFactory();
131-
assertThat(factory.getClass().getName()).contains("BasicAuthentication");
131+
void authenticated() throws Exception {
132+
TestRestTemplate restTemplate = new TestRestTemplate("user", "password");
133+
assertBasicAuthorizationCredentials(restTemplate, "user", "password");
132134
}
133135

134136
@Test
@@ -201,23 +203,25 @@ private Object mockArgument(Class<?> type) throws Exception {
201203
}
202204

203205
@Test
204-
void withBasicAuthAddsBasicAuthClientFactoryWhenNotAlreadyPresent() {
206+
void withBasicAuthAddsBasicAuthClientFactoryWhenNotAlreadyPresent() throws Exception {
205207
TestRestTemplate original = new TestRestTemplate();
206208
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
207209
assertThat(getConverterClasses(original)).containsExactlyElementsOf(getConverterClasses(basicAuth));
208-
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName()).contains("BasicAuth");
210+
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName())
211+
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
209212
assertThat(ReflectionTestUtils.getField(basicAuth.getRestTemplate().getRequestFactory(), "requestFactory"))
210213
.isInstanceOf(CustomHttpComponentsClientHttpRequestFactory.class);
211214
assertThat(basicAuth.getRestTemplate().getInterceptors()).isEmpty();
212215
assertBasicAuthorizationCredentials(basicAuth, "user", "password");
213216
}
214217

215218
@Test
216-
void withBasicAuthReplacesBasicAuthClientFactoryWhenAlreadyPresent() {
219+
void withBasicAuthReplacesBasicAuthClientFactoryWhenAlreadyPresent() throws Exception {
217220
TestRestTemplate original = new TestRestTemplate("foo", "bar").withBasicAuth("replace", "replace");
218221
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
219222
assertThat(getConverterClasses(basicAuth)).containsExactlyElementsOf(getConverterClasses(original));
220-
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName()).contains("BasicAuth");
223+
assertThat(basicAuth.getRestTemplate().getRequestFactory().getClass().getName())
224+
.contains("RestTemplateBuilderClientHttpRequestFactoryWrapper");
221225
assertThat(ReflectionTestUtils.getField(basicAuth.getRestTemplate().getRequestFactory(), "requestFactory"))
222226
.isInstanceOf(CustomHttpComponentsClientHttpRequestFactory.class);
223227
assertThat(basicAuth.getRestTemplate().getInterceptors()).isEmpty();
@@ -342,11 +346,12 @@ private void verifyRelativeUriHandling(TestRestTemplateCallback callback) throws
342346
}
343347

344348
private void assertBasicAuthorizationCredentials(TestRestTemplate testRestTemplate, String username,
345-
String password) {
349+
String password) throws Exception {
346350
ClientHttpRequestFactory requestFactory = testRestTemplate.getRestTemplate().getRequestFactory();
347-
Object authentication = ReflectionTestUtils.getField(requestFactory, "authentication");
348-
assertThat(authentication).hasFieldOrPropertyWithValue("username", username);
349-
assertThat(authentication).hasFieldOrPropertyWithValue("password", password);
351+
ClientHttpRequest request = requestFactory.createRequest(URI.create("http://localhost"), HttpMethod.POST);
352+
assertThat(request.getHeaders()).containsKeys(HttpHeaders.AUTHORIZATION);
353+
assertThat(request.getHeaders().get(HttpHeaders.AUTHORIZATION)).containsExactly(
354+
"Basic " + Base64Utils.encodeToString(String.format("%s:%s", username, password).getBytes()));
350355

351356
}
352357

@@ -356,16 +361,4 @@ private interface TestRestTemplateCallback {
356361

357362
}
358363

359-
static class TestClientHttpRequestFactory implements ClientHttpRequestFactory {
360-
361-
TestClientHttpRequestFactory(String value) {
362-
}
363-
364-
@Override
365-
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
366-
return null;
367-
}
368-
369-
}
370-
371364
}

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

+3-6
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@
1919
import java.nio.charset.Charset;
2020

2121
import org.springframework.http.HttpHeaders;
22-
import org.springframework.http.client.ClientHttpRequest;
2322
import org.springframework.util.Assert;
2423

2524
/**
26-
* Basic authentication properties which are used by
27-
* {@link BasicAuthenticationClientHttpRequestFactory}.
25+
* Basic authentication details to be applied to {@link HttpHeaders}.
2826
*
2927
* @author Dmytro Nosan
30-
* @see BasicAuthenticationClientHttpRequestFactory
28+
* @author Ilya Lukyanovich
3129
*/
3230
class BasicAuthentication {
3331

@@ -45,8 +43,7 @@ class BasicAuthentication {
4543
this.charset = charset;
4644
}
4745

48-
void applyTo(ClientHttpRequest request) {
49-
HttpHeaders headers = request.getHeaders();
46+
public void applyTo(HttpHeaders headers) {
5047
if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
5148
headers.setBasicAuth(this.username, this.password, this.charset);
5249
}

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

-53
This file was deleted.

0 commit comments

Comments
 (0)