Skip to content

Commit 51991d6

Browse files
Merge branch '3.2.x'
Closes gh-39536
2 parents b2c98a8 + 720e9ce commit 51991d6

File tree

9 files changed

+271
-15
lines changed

9 files changed

+271
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.web.client;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.boot.web.client.RestTemplateBuilder;
22+
import org.springframework.test.web.client.MockRestServiceServer;
23+
import org.springframework.web.client.RestClient;
24+
import org.springframework.web.client.RestClient.Builder;
25+
import org.springframework.web.client.RestTemplate;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
29+
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
30+
31+
/**
32+
* Tests for building a {@link RestClient} from a {@link RestTemplateBuilder}.
33+
*
34+
* @author Scott Frederick
35+
*/
36+
class RestClientWithRestTemplateBuilderTests {
37+
38+
@Test
39+
void buildUsingRestTemplateBuilderRootUri() {
40+
RestTemplate restTemplate = new RestTemplateBuilder().rootUri("https://resttemplate.example.com").build();
41+
RestClient.Builder builder = RestClient.builder(restTemplate);
42+
RestClient client = buildMockedClient(builder, "https://resttemplate.example.com/test");
43+
assertThat(client.get().uri("/test").retrieve().toBodilessEntity().getStatusCode().is2xxSuccessful()).isTrue();
44+
}
45+
46+
@Test
47+
void buildUsingRestClientBuilderBaseUrl() {
48+
RestTemplate restTemplate = new RestTemplateBuilder().build();
49+
RestClient.Builder builder = RestClient.builder(restTemplate).baseUrl("https://restclient.example.com");
50+
RestClient client = buildMockedClient(builder, "https://restclient.example.com/test");
51+
assertThat(client.get().uri("/test").retrieve().toBodilessEntity().getStatusCode().is2xxSuccessful()).isTrue();
52+
}
53+
54+
@Test
55+
void buildRestTemplateBuilderRootUriAndRestClientBuilderBaseUrl() {
56+
RestTemplate restTemplate = new RestTemplateBuilder().rootUri("https://resttemplate.example.com").build();
57+
RestClient.Builder builder = RestClient.builder(restTemplate).baseUrl("https://restclient.example.com");
58+
RestClient client = buildMockedClient(builder, "https://resttemplate.example.com/test");
59+
assertThat(client.get().uri("/test").retrieve().toBodilessEntity().getStatusCode().is2xxSuccessful()).isTrue();
60+
}
61+
62+
private RestClient buildMockedClient(Builder builder, String url) {
63+
MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build();
64+
server.expect(requestTo(url)).andRespond(withSuccess());
65+
return builder.build();
66+
}
67+
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.web.client;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.test.web.client.MockRestServiceServer;
22+
import org.springframework.web.client.RestClient;
23+
import org.springframework.web.client.RestClient.Builder;
24+
import org.springframework.web.client.RestTemplate;
25+
import org.springframework.web.util.DefaultUriBuilderFactory;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
29+
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
30+
31+
/**
32+
* Tests for building a {@link RestClient} from a {@link RestTemplate}.
33+
*
34+
* @author Scott Frederick
35+
*/
36+
class RestClientWithRestTemplateTests {
37+
38+
@Test
39+
void buildUsingRestTemplateUriTemplateHandler() {
40+
RestTemplate restTemplate = new RestTemplate();
41+
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("https://resttemplate.example.com");
42+
restTemplate.setUriTemplateHandler(uriBuilderFactory);
43+
Builder builder = RestClient.builder(restTemplate);
44+
RestClient client = buildMockedClient(builder, "https://resttemplate.example.com/test");
45+
assertThat(client.get().uri("/test").retrieve().toBodilessEntity().getStatusCode().is2xxSuccessful()).isTrue();
46+
}
47+
48+
@Test
49+
void buildUsingRestClientBuilderBaseUrl() {
50+
RestTemplate restTemplate = new RestTemplate();
51+
Builder builder = RestClient.builder(restTemplate).baseUrl("https://restclient.example.com");
52+
RestClient client = buildMockedClient(builder, "https://restclient.example.com/test");
53+
assertThat(client.get().uri("/test").retrieve().toBodilessEntity().getStatusCode().is2xxSuccessful()).isTrue();
54+
}
55+
56+
@Test
57+
void buildUsingRestTemplateUriTemplateHandlerAndRestClientBuilderBaseUrl() {
58+
RestTemplate restTemplate = new RestTemplate();
59+
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("https://resttemplate.example.com");
60+
restTemplate.setUriTemplateHandler(uriBuilderFactory);
61+
Builder builder = RestClient.builder(restTemplate).baseUrl("https://restclient.example.com");
62+
RestClient client = buildMockedClient(builder, "https://resttemplate.example.com/test");
63+
assertThat(client.get().uri("/test").retrieve().toBodilessEntity().getStatusCode().is2xxSuccessful()).isTrue();
64+
}
65+
66+
private RestClient buildMockedClient(Builder builder, String url) {
67+
MockRestServiceServer server = MockRestServiceServer.bindTo(builder).build();
68+
server.expect(requestTo(url)).andRespond(withSuccess());
69+
return builder.build();
70+
}
71+
72+
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,8 @@ public void setUriTemplateHandler(UriTemplateHandler handler) {
171171
}
172172

173173
/**
174-
* Returns the root URI applied by a {@link RootUriTemplateHandler} or {@code ""} if
175-
* the root URI is not available.
174+
* Returns the root URI applied by {@link RestTemplateBuilder#rootUri(String)} or
175+
* {@code ""} if the root URI has not been applied.
176176
* @return the root URI
177177
*/
178178
public String getRootUri() {

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

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -150,10 +150,8 @@ public RestTemplateBuilder detectRequestFactory(boolean detectRequestFactory) {
150150

151151
/**
152152
* Set a root URL that should be applied to each request that starts with {@code '/'}.
153-
* Since this works by adding a {@link UriTemplateHandler} to the
154-
* {@link RestTemplate}, the root URL will only apply when {@code String} variants of
155-
* the {@link RestTemplate} methods are used for specifying the request URL. See
156-
* {@link RootUriTemplateHandler} for details.
153+
* The root URL will only apply when {@code String} variants of the
154+
* {@link RestTemplate} methods are used for specifying the request URL.
157155
* @param rootUri the root URI or {@code null}
158156
* @return a new builder instance
159157
*/
@@ -639,7 +637,7 @@ public <T extends RestTemplate> T configure(T restTemplate) {
639637
restTemplate.setErrorHandler(this.errorHandler);
640638
}
641639
if (this.rootUri != null) {
642-
RootUriTemplateHandler.addTo(restTemplate, this.rootUri);
640+
RootUriBuilderFactory.applyTo(restTemplate, this.rootUri);
643641
}
644642
restTemplate.getInterceptors().addAll(this.interceptors);
645643
if (!CollectionUtils.isEmpty(this.customizers)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.web.client;
18+
19+
import org.springframework.util.Assert;
20+
import org.springframework.web.client.RestTemplate;
21+
import org.springframework.web.util.UriBuilder;
22+
import org.springframework.web.util.UriBuilderFactory;
23+
import org.springframework.web.util.UriComponentsBuilder;
24+
import org.springframework.web.util.UriTemplateHandler;
25+
26+
/**
27+
* {@link UriBuilderFactory} to set the root for URI that starts with {@code '/'}.
28+
*
29+
* @author Scott Frederick
30+
* @since 3.2.3
31+
*/
32+
public class RootUriBuilderFactory extends RootUriTemplateHandler implements UriBuilderFactory {
33+
34+
@SuppressWarnings("removal")
35+
RootUriBuilderFactory(String rootUri) {
36+
super(rootUri);
37+
}
38+
39+
@SuppressWarnings("removal")
40+
RootUriBuilderFactory(String rootUri, UriTemplateHandler delegate) {
41+
super(rootUri, delegate);
42+
}
43+
44+
@Override
45+
public UriBuilder uriString(String uriTemplate) {
46+
return UriComponentsBuilder.fromUriString(apply(uriTemplate));
47+
}
48+
49+
@Override
50+
public UriBuilder builder() {
51+
return UriComponentsBuilder.newInstance();
52+
}
53+
54+
/**
55+
* Apply a {@link RootUriBuilderFactory} instance to the given {@link RestTemplate}.
56+
* @param restTemplate the {@link RestTemplate} to add the builder factory to
57+
* @param rootUri the root URI
58+
*/
59+
static void applyTo(RestTemplate restTemplate, String rootUri) {
60+
Assert.notNull(restTemplate, "RestTemplate must not be null");
61+
RootUriBuilderFactory handler = new RootUriBuilderFactory(rootUri, restTemplate.getUriTemplateHandler());
62+
restTemplate.setUriTemplateHandler(handler);
63+
}
64+
65+
}

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

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
* {@link UriTemplateHandler} to set the root for URI that starts with {@code '/'}.
3131
*
3232
* @author Phillip Webb
33+
* @author Scott Frederick
3334
* @since 1.4.0
3435
*/
3536
public class RootUriTemplateHandler implements UriTemplateHandler {
@@ -47,16 +48,20 @@ protected RootUriTemplateHandler(UriTemplateHandler handler) {
4748
/**
4849
* Create a new {@link RootUriTemplateHandler} instance.
4950
* @param rootUri the root URI to be used to prefix relative URLs
51+
* @deprecated since 3.2.3 for removal in 3.4.0, with no replacement
5052
*/
53+
@Deprecated(since = "3.2.3", forRemoval = true)
5154
public RootUriTemplateHandler(String rootUri) {
5255
this(rootUri, new DefaultUriBuilderFactory());
5356
}
5457

5558
/**
5659
* Create a new {@link RootUriTemplateHandler} instance.
5760
* @param rootUri the root URI to be used to prefix relative URLs
58-
* @param handler the delegate handler
61+
* @param handler the handler handler
62+
* @deprecated since 3.2.3 for removal in 3.4.0, with no replacement
5963
*/
64+
@Deprecated(since = "3.2.3", forRemoval = true)
6065
public RootUriTemplateHandler(String rootUri, UriTemplateHandler handler) {
6166
Assert.notNull(rootUri, "RootUri must not be null");
6267
Assert.notNull(handler, "Handler must not be null");
@@ -74,7 +79,7 @@ public URI expand(String uriTemplate, Object... uriVariables) {
7479
return this.handler.expand(apply(uriTemplate), uriVariables);
7580
}
7681

77-
private String apply(String uriTemplate) {
82+
String apply(String uriTemplate) {
7883
if (StringUtils.startsWithIgnoreCase(uriTemplate, "/")) {
7984
return getRootUri() + uriTemplate;
8085
}
@@ -91,7 +96,9 @@ public String getRootUri() {
9196
* @param wrapper the wrapper to apply to the delegate URI template handler
9297
* @return the new handler
9398
* @since 2.3.10
99+
* @deprecated since 3.2.3 for removal in 3.4.0, with no replacement
94100
*/
101+
@Deprecated(since = "3.2.3", forRemoval = true)
95102
public RootUriTemplateHandler withHandlerWrapper(Function<UriTemplateHandler, UriTemplateHandler> wrapper) {
96103
return new RootUriTemplateHandler(getRootUri(), wrapper.apply(this.handler));
97104
}
@@ -101,7 +108,9 @@ public RootUriTemplateHandler withHandlerWrapper(Function<UriTemplateHandler, Ur
101108
* @param restTemplate the {@link RestTemplate} to add the handler to
102109
* @param rootUri the root URI
103110
* @return the added {@link RootUriTemplateHandler}.
111+
* @deprecated since 3.2.3 for removal in 3.4.0, with no replacement
104112
*/
113+
@Deprecated(since = "3.2.3", forRemoval = true)
105114
public static RootUriTemplateHandler addTo(RestTemplate restTemplate, String rootUri) {
106115
Assert.notNull(restTemplate, "RestTemplate must not be null");
107116
RootUriTemplateHandler handler = new RootUriTemplateHandler(rootUri, restTemplate.getUriTemplateHandler());

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -126,7 +126,7 @@ void rootUriShouldApplyAfterUriTemplateHandler() {
126126
.build();
127127
UriTemplateHandler handler = template.getUriTemplateHandler();
128128
handler.expand("/hello");
129-
assertThat(handler).isInstanceOf(RootUriTemplateHandler.class);
129+
assertThat(handler).isInstanceOf(RootUriBuilderFactory.class);
130130
then(uriTemplateHandler).should().expand("https://example.com/hello");
131131
}
132132

@@ -439,7 +439,7 @@ void customizerShouldBeAppliedAtTheEnd() {
439439
.customizers((restTemplate) -> {
440440
assertThat(restTemplate.getInterceptors()).hasSize(1);
441441
assertThat(restTemplate.getMessageConverters()).contains(this.messageConverter);
442-
assertThat(restTemplate.getUriTemplateHandler()).isInstanceOf(RootUriTemplateHandler.class);
442+
assertThat(restTemplate.getUriTemplateHandler()).isInstanceOf(RootUriBuilderFactory.class);
443443
assertThat(restTemplate.getErrorHandler()).isEqualTo(errorHandler);
444444
ClientHttpRequestFactory actualRequestFactory = restTemplate.getRequestFactory();
445445
assertThat(actualRequestFactory).isInstanceOf(InterceptingClientHttpRequestFactory.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.web.client;
18+
19+
import java.net.URI;
20+
import java.net.URISyntaxException;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import org.springframework.web.util.UriBuilder;
25+
import org.springframework.web.util.UriBuilderFactory;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* Tests for {@link RootUriBuilderFactory}.
31+
*
32+
* @author Scott Frederick
33+
*/
34+
class RootUriBuilderFactoryTests {
35+
36+
@Test
37+
void uriStringPrefixesRoot() throws URISyntaxException {
38+
UriBuilderFactory builderFactory = new RootUriBuilderFactory("https://example.com");
39+
UriBuilder builder = builderFactory.uriString("/hello");
40+
assertThat(builder.build()).isEqualTo(new URI("https://example.com/hello"));
41+
}
42+
43+
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
4343
* @author Phillip Webb
4444
*/
4545
@ExtendWith(MockitoExtension.class)
46+
@SuppressWarnings("removal")
4647
class RootUriTemplateHandlerTests {
4748

4849
private URI uri;

0 commit comments

Comments
 (0)