Add `redirects(...)` method to `RestTemplateBuilder`

Add `redirects(...)` method to `RestTemplateBuilder` to allow redirect
customization. This new method is required in 3.4 since the default
redirect strategy for some clients has changed and users need a way
to restore the old behavior.

See gh-43258
This commit is contained in:
Yanming Zhou 2024-11-22 17:12:46 +08:00 committed by Phillip Webb
parent 916efb632c
commit f450b28a7c
4 changed files with 65 additions and 1 deletions

View File

@ -47,6 +47,7 @@ import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.client.RootUriTemplateHandler;
import org.springframework.core.ParameterizedTypeReference;
@ -92,6 +93,7 @@ import org.springframework.web.util.UriTemplateHandler;
* @author Andy Wilkinson
* @author Kristine Jetzke
* @author Dmytro Nosan
* @author Yanming Zhou
* @since 1.4.0
*/
public class TestRestTemplate {
@ -946,6 +948,22 @@ public class TestRestTemplate {
return template;
}
/**
* Creates a new {@code TestRestTemplate} with the same configuration as this one,
* except that it will use the given {@code redirects} strategies. The request factory
* used is a new instance of the underlying {@link RestTemplate}'s request factory
* type (when possible).
* @param redirects the redirects
* @return the new template
* @since 3.4.1
*/
public TestRestTemplate withRedirects(Redirects redirects) {
TestRestTemplate template = new TestRestTemplate(this.builder.redirects(redirects), null, null,
this.httpClientOptions);
template.setUriTemplateHandler(getRestTemplate().getUriTemplateHandler());
return template;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private RequestEntity<?> createRequestEntityWithRootAppliedUri(RequestEntity<?> requestEntity) {
return new RequestEntity(requestEntity.getBody(), requestEntity.getHeaders(), requestEntity.getMethod(),
@ -988,7 +1006,10 @@ public class TestRestTemplate {
/**
* Enable redirects.
* @deprecated since 3.4.1 for removal in 3.6.0 in favor of
* {@link #withRedirects(Redirects)}
*/
@Deprecated(since = "3.4.1", forRemoval = true)
ENABLE_REDIRECTS,
/**
@ -1032,7 +1053,8 @@ public class TestRestTemplate {
Set<HttpClientOption> options = new HashSet<>(Arrays.asList(httpClientOptions));
this.cookieSpec = (options.contains(HttpClientOption.ENABLE_COOKIES) ? StandardCookieSpec.STRICT
: StandardCookieSpec.IGNORE);
this.enableRedirects = options.contains(HttpClientOption.ENABLE_REDIRECTS);
this.enableRedirects = options.contains(HttpClientOption.ENABLE_REDIRECTS)
|| settings.redirects() != Redirects.DONT_FOLLOW;
boolean ssl = options.contains(HttpClientOption.SSL);
if (settings.readTimeout() != null || ssl) {
setHttpClient(createHttpClient(settings.readTimeout(), ssl));

View File

@ -26,6 +26,7 @@ import java.util.stream.Stream;
import org.apache.hc.client5.http.config.RequestConfig;
import org.junit.jupiter.api.Test;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
import org.springframework.boot.test.web.client.TestRestTemplate.CustomHttpComponentsClientHttpRequestFactory;
import org.springframework.boot.test.web.client.TestRestTemplate.HttpClientOption;
import org.springframework.boot.web.client.RestTemplateBuilder;
@ -66,6 +67,7 @@ import static org.mockito.Mockito.mock;
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Kristine Jetzke
* @author Yanming Zhou
*/
class TestRestTemplateTests {
@ -129,6 +131,7 @@ class TestRestTemplateTests {
assertBasicAuthorizationCredentials(restTemplate, "user", "password");
}
@SuppressWarnings("deprecation")
@Test
void options() {
TestRestTemplate template = new TestRestTemplate(HttpClientOption.ENABLE_REDIRECTS);
@ -139,6 +142,21 @@ class TestRestTemplateTests {
assertThat(config.isRedirectsEnabled()).isTrue();
}
@Test
void redirects() {
TestRestTemplate template = new TestRestTemplate().withRedirects(Redirects.DONT_FOLLOW);
CustomHttpComponentsClientHttpRequestFactory factory = (CustomHttpComponentsClientHttpRequestFactory) template
.getRestTemplate()
.getRequestFactory();
RequestConfig config = factory.createRequestConfig();
assertThat(config.isRedirectsEnabled()).isFalse();
template = new TestRestTemplate().withRedirects(Redirects.FOLLOW);
factory = (CustomHttpComponentsClientHttpRequestFactory) template.getRestTemplate().getRequestFactory();
config = factory.createRequestConfig();
assertThat(config.isRedirectsEnabled()).isTrue();
}
@Test
void restOperationsAreAvailable() {
RestTemplate delegate = mock(RestTemplate.class);

View File

@ -33,6 +33,7 @@ import java.util.function.Supplier;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
import org.springframework.boot.ssl.SslBundle;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
@ -64,6 +65,7 @@ import org.springframework.web.util.UriTemplateHandler;
* @author Kevin Strijbos
* @author Ilya Lukyanovich
* @author Scott Frederick
* @author Yanming Zhou
* @since 1.4.0
*/
public class RestTemplateBuilder {
@ -501,6 +503,19 @@ public class RestTemplateBuilder {
this.defaultHeaders, this.customizers, this.requestCustomizers);
}
/**
* Sets the redirect strategy on the underlying {@link ClientHttpRequestFactory}.
* @param redirects the redirect strategy
* @return a new builder instance.
* @since 3.4.1
*/
public RestTemplateBuilder redirects(Redirects redirects) {
return new RestTemplateBuilder(this.requestFactorySettings.withRedirects(redirects), this.detectRequestFactory,
this.rootUri, this.messageConverters, this.interceptors, this.requestFactoryBuilder,
this.uriTemplateHandler, this.errorHandler, this.basicAuthentication, this.defaultHeaders,
this.customizers, this.requestCustomizers);
}
/**
* Sets the SSL bundle on the underlying {@link ClientHttpRequestFactory}.
* @param sslBundle the SSL bundle

View File

@ -32,6 +32,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings.Redirects;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
@ -73,6 +74,7 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
* @author Kevin Strijbos
* @author Ilya Lukyanovich
* @author Brian Clozel
* @author Yanming Zhou
*/
@ExtendWith(MockitoExtension.class)
class RestTemplateBuilderTests {
@ -486,6 +488,13 @@ class RestTemplateBuilderTests {
assertThat(template.getRequestFactory()).isInstanceOf(BufferingClientHttpRequestFactory.class);
}
@Test
void configureRedirects() {
assertThat(this.builder.redirects(Redirects.DONT_FOLLOW)).extracting("requestFactorySettings")
.extracting("redirects")
.isSameAs(Redirects.DONT_FOLLOW);
}
private ClientHttpRequest createRequest(RestTemplate template) {
return ReflectionTestUtils.invokeMethod(template, "createRequest", URI.create("http://localhost"),
HttpMethod.GET);