Skip to content

Commit f978051

Browse files
committed
Merge pull request #36213 from poutsma
* pr/36213: Add RestClient SSL support Overhaul reference documentation for RestClient Add RestClient HttpMessageConverters support Polish 'Add initial support for RestClient' Add initial support for RestClient Closes gh-36213
2 parents 8b3070e + cfdc173 commit f978051

File tree

22 files changed

+1048
-84
lines changed

22 files changed

+1048
-84
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2012-2023 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.autoconfigure.web.client;
18+
19+
import java.util.function.Consumer;
20+
21+
import org.springframework.boot.ssl.SslBundle;
22+
import org.springframework.boot.ssl.SslBundles;
23+
import org.springframework.boot.web.client.ClientHttpRequestFactories;
24+
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
25+
import org.springframework.http.client.ClientHttpRequestFactory;
26+
import org.springframework.web.client.RestClient;
27+
28+
/**
29+
* An auto-configured {@link RestClientSsl} implementation.
30+
*
31+
* @author Phillip Webb
32+
*/
33+
class AutoConfiguredRestClientSsl implements RestClientSsl {
34+
35+
private final SslBundles sslBundles;
36+
37+
AutoConfiguredRestClientSsl(SslBundles sslBundles) {
38+
this.sslBundles = sslBundles;
39+
}
40+
41+
@Override
42+
public Consumer<RestClient.Builder> fromBundle(String bundleName) {
43+
return fromBundle(this.sslBundles.getBundle(bundleName));
44+
}
45+
46+
@Override
47+
public Consumer<RestClient.Builder> fromBundle(SslBundle bundle) {
48+
return (builder) -> {
49+
ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS.withSslBundle(bundle);
50+
ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings);
51+
builder.requestFactory(requestFactory);
52+
};
53+
}
54+
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2012-2023 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.autoconfigure.web.client;
18+
19+
import java.util.Arrays;
20+
import java.util.List;
21+
22+
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
23+
import org.springframework.boot.web.client.RestClientCustomizer;
24+
import org.springframework.http.converter.HttpMessageConverter;
25+
import org.springframework.util.Assert;
26+
import org.springframework.web.client.RestClient;
27+
28+
/**
29+
* {@link RestClientCustomizer} to apply {@link HttpMessageConverter
30+
* HttpMessageConverters}.
31+
*
32+
* @author Phillip Webb
33+
* @since 3.2.0
34+
*/
35+
public class HttpMessageConvertersRestClientCustomizer implements RestClientCustomizer {
36+
37+
private final Iterable<? extends HttpMessageConverter<?>> messageConverters;
38+
39+
public HttpMessageConvertersRestClientCustomizer(HttpMessageConverter<?>... messageConverters) {
40+
Assert.notNull(messageConverters, "MessageConverters must not be null");
41+
this.messageConverters = Arrays.asList(messageConverters);
42+
}
43+
44+
HttpMessageConvertersRestClientCustomizer(HttpMessageConverters messageConverters) {
45+
this.messageConverters = messageConverters;
46+
}
47+
48+
@Override
49+
public void customize(RestClient.Builder restClientBuilder) {
50+
restClientBuilder.messageConverters(this::configureMessageConverters);
51+
}
52+
53+
private void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
54+
if (this.messageConverters != null) {
55+
messageConverters.clear();
56+
this.messageConverters.forEach(messageConverters::add);
57+
}
58+
}
59+
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2012-2023 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.autoconfigure.web.client;
18+
19+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
20+
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
21+
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
22+
23+
/**
24+
* {@link SpringBootCondition} that applies only when running in a non-reactive web
25+
* application.
26+
*
27+
* @author Phillip Webb
28+
*/
29+
class NotReactiveWebApplicationCondition extends NoneNestedConditions {
30+
31+
NotReactiveWebApplicationCondition() {
32+
super(ConfigurationPhase.PARSE_CONFIGURATION);
33+
}
34+
35+
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
36+
private static class ReactiveWebApplication {
37+
38+
}
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2012-2023 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.autoconfigure.web.client;
18+
19+
import org.springframework.beans.factory.ObjectProvider;
20+
import org.springframework.boot.autoconfigure.AutoConfiguration;
21+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24+
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
25+
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
26+
import org.springframework.boot.web.client.ClientHttpRequestFactories;
27+
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
28+
import org.springframework.boot.web.client.RestClientCustomizer;
29+
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.Conditional;
31+
import org.springframework.context.annotation.Scope;
32+
import org.springframework.core.Ordered;
33+
import org.springframework.core.annotation.Order;
34+
import org.springframework.web.client.RestClient;
35+
36+
/**
37+
* {@link EnableAutoConfiguration Auto-configuration} for {@link RestClient}.
38+
* <p>
39+
* This will produce a {@link org.springframework.web.client.RestClient.Builder
40+
* RestClient.Builder} bean with the {@code prototype} scope, meaning each injection point
41+
* will receive a newly cloned instance of the builder.
42+
*
43+
* @author Arjen Poutsma
44+
* @since 3.2.0
45+
*/
46+
@AutoConfiguration(after = HttpMessageConvertersAutoConfiguration.class)
47+
@ConditionalOnClass(RestClient.class)
48+
@Conditional(NotReactiveWebApplicationCondition.class)
49+
public class RestClientAutoConfiguration {
50+
51+
@Bean
52+
@ConditionalOnMissingBean
53+
@Order(Ordered.LOWEST_PRECEDENCE)
54+
public HttpMessageConvertersRestClientCustomizer httpMessageConvertersRestClientCustomizer(
55+
ObjectProvider<HttpMessageConverters> messageConverters) {
56+
return new HttpMessageConvertersRestClientCustomizer(messageConverters.getIfUnique());
57+
}
58+
59+
@Bean
60+
@Scope("prototype")
61+
@ConditionalOnMissingBean
62+
public RestClient.Builder webClientBuilder(ObjectProvider<RestClientCustomizer> customizerProvider) {
63+
RestClient.Builder builder = RestClient.builder()
64+
.requestFactory(ClientHttpRequestFactories.get(ClientHttpRequestFactorySettings.DEFAULTS));
65+
customizerProvider.orderedStream().forEach((customizer) -> customizer.customize(builder));
66+
return builder;
67+
}
68+
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2012-2023 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.autoconfigure.web.client;
18+
19+
import java.util.function.Consumer;
20+
21+
import org.springframework.boot.ssl.NoSuchSslBundleException;
22+
import org.springframework.boot.ssl.SslBundle;
23+
import org.springframework.boot.web.client.ClientHttpRequestFactories;
24+
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
25+
import org.springframework.http.client.ClientHttpRequestFactory;
26+
import org.springframework.web.client.RestClient;
27+
28+
/**
29+
* Interface that can be used to {@link RestClient.Builder#apply apply} SSL configuration
30+
* to a {@link org.springframework.web.client.RestClient.Builder RestClient.Builder}.
31+
* <p>
32+
* Typically used as follows: <pre class="code">
33+
* &#064;Bean
34+
* public MyBean myBean(RestClient.Builder restClientBuilder, RestClientSsl ssl) {
35+
* RestClient restClientrestClient= restClientBuilder.apply(ssl.forBundle("mybundle")).build();
36+
* return new MyBean(webClient);
37+
* }
38+
* </pre> NOTE: Apply SSL configuration will replace any previously
39+
* {@link RestClient.Builder#requestFactory configured} {@link ClientHttpRequestFactory}.
40+
* If you need to configure {@link ClientHttpRequestFactory} with more than just SSL
41+
* consider using a {@link ClientHttpRequestFactorySettings} with
42+
* {@link ClientHttpRequestFactories}.
43+
*
44+
* @author Phillip Webb
45+
* @since 3.2.0
46+
*/
47+
public interface RestClientSsl {
48+
49+
/**
50+
* Return a {@link Consumer} that will apply SSL configuration for the named
51+
* {@link SslBundle} to a {@link org.springframework.web.client.RestClient.Builder
52+
* RestClient.Builder}.
53+
* @param bundleName the name of the SSL bundle to apply
54+
* @return a {@link Consumer} to apply the configuration
55+
* @throws NoSuchSslBundleException if a bundle with the provided name does not exist
56+
*/
57+
Consumer<RestClient.Builder> fromBundle(String bundleName) throws NoSuchSslBundleException;
58+
59+
/**
60+
* Return a {@link Consumer} that will apply SSL configuration for the
61+
* {@link SslBundle} to a {@link org.springframework.web.client.RestClient.Builder
62+
* RestClient.Builder}.
63+
* @param bundle the SSL bundle to apply
64+
* @return a {@link Consumer} to apply the configuration
65+
*/
66+
Consumer<RestClient.Builder> fromBundle(SslBundle bundle);
67+
68+
}

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

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,8 @@
2121
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2222
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2323
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24-
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
25-
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
26-
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
2724
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
2825
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
29-
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition;
3026
import org.springframework.boot.web.client.RestTemplateBuilder;
3127
import org.springframework.boot.web.client.RestTemplateCustomizer;
3228
import org.springframework.boot.web.client.RestTemplateRequestCustomizer;
@@ -69,17 +65,4 @@ public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer res
6965
return restTemplateBuilderConfigurer.configure(builder);
7066
}
7167

72-
static class NotReactiveWebApplicationCondition extends NoneNestedConditions {
73-
74-
NotReactiveWebApplicationCondition() {
75-
super(ConfigurationPhase.PARSE_CONFIGURATION);
76-
}
77-
78-
@ConditionalOnWebApplication(type = Type.REACTIVE)
79-
private static class ReactiveWebApplication {
80-
81-
}
82-
83-
}
84-
8568
}

0 commit comments

Comments
 (0)