Skip to content

Commit 5864f57

Browse files
committed
Resolve URI to baseUrl in RestClient
Closes gh-32679
1 parent 98e89d8 commit 5864f57

File tree

5 files changed

+87
-2
lines changed

5 files changed

+87
-2
lines changed

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,13 @@ public RequestBodySpec uri(Function<UriBuilder, URI> uriFunction) {
341341

342342
@Override
343343
public RequestBodySpec uri(URI uri) {
344-
this.uri = uri;
344+
if (uri.isAbsolute()) {
345+
this.uri = uri;
346+
}
347+
else {
348+
URI baseUri = DefaultRestClient.this.uriBuilderFactory.expand("");
349+
this.uri = baseUri.resolve(uri);
350+
}
345351
return this;
346352
}
347353

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

+7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.web.client;
1818

19+
import java.net.URI;
1920
import java.util.ArrayList;
2021
import java.util.Arrays;
2122
import java.util.LinkedHashMap;
@@ -245,6 +246,12 @@ public RestClient.Builder baseUrl(String baseUrl) {
245246
return this;
246247
}
247248

249+
@Override
250+
public RestClient.Builder baseUrl(URI baseUrl) {
251+
this.baseUrl = baseUrl.toString();
252+
return this;
253+
}
254+
248255
@Override
249256
public RestClient.Builder defaultUriVariables(Map<String, ?> defaultUriVariables) {
250257
this.defaultUriVariables = defaultUriVariables;

spring-web/src/main/java/org/springframework/web/client/RestClient.java

+36-1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,17 @@ static RestClient create(String baseUrl) {
154154
return new DefaultRestClientBuilder().baseUrl(baseUrl).build();
155155
}
156156

157+
/**
158+
* Variant of {@link #create()} that accepts a default base {@code URI}. For more
159+
* details see {@link Builder#baseUrl(URI) Builder.baseUrl(URI)}.
160+
* @param baseUrl the base URI for all requests
161+
* @since 6.2
162+
* @see #builder()
163+
*/
164+
static RestClient create(URI baseUrl) {
165+
return new DefaultRestClientBuilder().baseUrl(baseUrl).build();
166+
}
167+
157168
/**
158169
* Create a new {@code RestClient} based on the configuration of the given
159170
* {@code RestTemplate}.
@@ -230,6 +241,26 @@ interface Builder {
230241
*/
231242
Builder baseUrl(String baseUrl);
232243

244+
/**
245+
* Configure a base {@code URI} for requests. Effectively a shortcut for:
246+
* <pre class="code">
247+
* URI baseUrl = URI.create("https://abc.go.com/v1");
248+
* DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl.toString());
249+
* RestClient client = RestClient.builder().uriBuilderFactory(factory).build();
250+
* </pre>
251+
* <p>The {@code DefaultUriBuilderFactory} is used to prepare the URL
252+
* for every request with the given base URL, unless the URL request
253+
* for a given URL is absolute in which case the base URL is ignored.
254+
* <p><strong>Note:</strong> this method is mutually exclusive with
255+
* {@link #uriBuilderFactory(UriBuilderFactory)}. If both are used, the
256+
* {@code baseUrl} value provided here will be ignored.
257+
* @return this builder
258+
* @since 6.2
259+
* @see DefaultUriBuilderFactory#DefaultUriBuilderFactory(String)
260+
* @see #uriBuilderFactory(UriBuilderFactory)
261+
*/
262+
Builder baseUrl(URI baseUrl);
263+
233264
/**
234265
* Configure default URL variable values to use when expanding URI
235266
* templates with a {@link Map}. Effectively a shortcut for:
@@ -414,7 +445,11 @@ Builder defaultStatusHandler(Predicate<HttpStatusCode> statusPredicate,
414445
interface UriSpec<S extends RequestHeadersSpec<?>> {
415446

416447
/**
417-
* Specify the URI using an absolute, fully constructed {@link URI}.
448+
* Specify the URI using a fully constructed {@link URI}.
449+
* <p>If the given URI is absolute, it is used as given. If it is
450+
* a relative URI, the {@link UriBuilderFactory} configured for
451+
* the client (e.g. with a base URI) will be used to
452+
* {@linkplain URI#resolve(URI) resolve} the given URI against.
418453
*/
419454
S uri(URI uri);
420455

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

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.client;
1818

1919
import java.lang.reflect.Field;
20+
import java.net.URI;
2021
import java.util.List;
2122

2223
import org.junit.jupiter.api.Test;
@@ -90,6 +91,18 @@ void defaultUriBuilderFactory() {
9091
assertThat(fieldValue("uriBuilderFactory", defaultBuilder)).isNull();
9192
}
9293

94+
@Test
95+
void defaultUri() {
96+
URI baseUrl = URI.create("https://example.org");
97+
RestClient.Builder builder = RestClient.builder();
98+
builder.baseUrl(baseUrl);
99+
100+
assertThat(builder).isInstanceOf(DefaultRestClientBuilder.class);
101+
DefaultRestClientBuilder defaultBuilder = (DefaultRestClientBuilder) builder;
102+
103+
assertThat(fieldValue("baseUrl", defaultBuilder)).isEqualTo(baseUrl.toString());
104+
}
105+
93106
@Nullable
94107
private static Object fieldValue(String name, DefaultRestClientBuilder instance) {
95108
try {

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

+24
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.lang.annotation.Retention;
2222
import java.lang.annotation.RetentionPolicy;
2323
import java.lang.annotation.Target;
24+
import java.net.URI;
25+
import java.net.URISyntaxException;
2426
import java.nio.charset.StandardCharsets;
2527
import java.util.List;
2628
import java.util.Map;
@@ -923,6 +925,28 @@ void defaultRequestOverride(ClientHttpRequestFactory requestFactory) {
923925
expectRequest(request -> assertThat(request.getHeader("Accept")).isEqualTo(MediaType.TEXT_PLAIN_VALUE));
924926
}
925927

928+
@ParameterizedRestClientTest
929+
void relativeUri(ClientHttpRequestFactory requestFactory) throws URISyntaxException {
930+
startServer(requestFactory);
931+
932+
prepareResponse(response -> response.setHeader("Content-Type", "text/plain")
933+
.setBody("Hello Spring!"));
934+
935+
URI uri = new URI(null, null, "/foo bar", null);
936+
937+
String result = this.restClient
938+
.get()
939+
.uri(uri)
940+
.accept(MediaType.TEXT_PLAIN)
941+
.retrieve()
942+
.body(String.class);
943+
944+
assertThat(result).isEqualTo("Hello Spring!");
945+
946+
expectRequestCount(1);
947+
expectRequest(request -> assertThat(request.getPath()).isEqualTo("/foo%20bar"));
948+
}
949+
926950

927951
private void prepareResponse(Consumer<MockResponse> consumer) {
928952
MockResponse response = new MockResponse();

0 commit comments

Comments
 (0)