Skip to content

Commit 5c73f29

Browse files
committed
Add hypermedia autoconfiguration for WebClient.
* Provide CodecCustomizer to support WebClient. Related: spring-projects#16020
1 parent 533a20a commit 5c73f29

File tree

4 files changed

+96
-6
lines changed

4 files changed

+96
-6
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/hateoas/HypermediaAutoConfiguration.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616

1717
package org.springframework.boot.autoconfigure.hateoas;
1818

19-
import com.fasterxml.jackson.databind.ObjectMapper;
19+
import java.util.List;
2020

21+
import org.springframework.beans.factory.ObjectProvider;
2122
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
2223
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -28,23 +29,36 @@
2829
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
2930
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
3031
import org.springframework.boot.context.properties.EnableConfigurationProperties;
32+
import org.springframework.boot.web.client.RestTemplateCustomizer;
33+
import org.springframework.boot.web.codec.CodecCustomizer;
34+
import org.springframework.context.annotation.Bean;
3135
import org.springframework.context.annotation.Configuration;
3236
import org.springframework.context.annotation.Import;
3337
import org.springframework.hateoas.EntityModel;
3438
import org.springframework.hateoas.client.LinkDiscoverers;
3539
import org.springframework.hateoas.config.EnableHypermediaSupport;
3640
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
41+
import org.springframework.hateoas.config.HypermediaMappingInformation;
42+
import org.springframework.http.codec.json.Jackson2JsonDecoder;
43+
import org.springframework.http.codec.json.Jackson2JsonEncoder;
3744
import org.springframework.plugin.core.Plugin;
45+
import org.springframework.util.Assert;
46+
import org.springframework.util.MimeType;
3847
import org.springframework.web.bind.annotation.RequestMapping;
48+
import org.springframework.web.client.RestTemplate;
49+
import org.springframework.web.reactive.function.client.WebClient;
3950
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
4051

52+
import com.fasterxml.jackson.databind.ObjectMapper;
53+
4154
/**
4255
* {@link EnableAutoConfiguration Auto-configuration} for Spring HATEOAS's
4356
* {@link EnableHypermediaSupport @EnableHypermediaSupport}.
4457
*
4558
* @author Roy Clarkson
4659
* @author Oliver Gierke
4760
* @author Andy Wilkinson
61+
* @author Greg Turnquist
4862
* @since 1.1.0
4963
*/
5064
@Configuration(proxyBeanMethods = false)
@@ -64,4 +78,30 @@ protected static class HypermediaConfiguration {
6478

6579
}
6680

81+
@Configuration(proxyBeanMethods = false)
82+
@ConditionalOnClass(WebClient.class)
83+
protected static class WebClientHypermediaConfiguration {
84+
85+
@Bean
86+
CodecCustomizer hypermediaCodecCustomizer(ObjectProvider<ObjectMapper> mapperProvider,
87+
List<HypermediaMappingInformation> hypermediaTypes) {
88+
return codecConfigurer -> {
89+
Assert.notNull(hypermediaTypes, "HypermediaMappingInformations must not be null!");
90+
91+
hypermediaTypes.forEach(hypermedia -> {
92+
93+
ObjectMapper objectMapper = hypermedia
94+
.configureObjectMapper(mapperProvider.getIfAvailable(ObjectMapper::new).copy());
95+
MimeType[] mimeTypes = hypermedia.getMediaTypes().toArray(new MimeType[0]);
96+
97+
codecConfigurer.customCodecs()
98+
.registerWithDefaultConfig(new Jackson2JsonEncoder(objectMapper, mimeTypes));
99+
codecConfigurer.customCodecs()
100+
.registerWithDefaultConfig(new Jackson2JsonDecoder(objectMapper, mimeTypes));
101+
});
102+
};
103+
}
104+
105+
}
106+
67107
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
3030
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
3131
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
32+
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
3233
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
3334
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
3435
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.NotReactiveWebApplicationCondition;
@@ -49,7 +50,7 @@
4950
* @since 1.4.0
5051
*/
5152
@Configuration(proxyBeanMethods = false)
52-
@AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
53+
@AutoConfigureAfter({ HttpMessageConvertersAutoConfiguration.class, HypermediaAutoConfiguration.class })
5354
@ConditionalOnClass(RestTemplate.class)
5455
@Conditional(NotReactiveWebApplicationCondition.class)
5556
public class RestTemplateAutoConfiguration {

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hateoas/HypermediaAutoConfigurationTests.java

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@
1616

1717
package org.springframework.boot.autoconfigure.hateoas;
1818

19+
import static org.assertj.core.api.Assertions.*;
20+
21+
import java.util.Map;
1922
import java.util.Optional;
2023

2124
import org.junit.jupiter.api.Test;
22-
2325
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
2426
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration.HypermediaConfiguration;
2527
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
2628
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
29+
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
2730
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
2831
import org.springframework.boot.test.context.FilteredClassLoader;
2932
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
33+
import org.springframework.boot.web.codec.CodecCustomizer;
3034
import org.springframework.context.annotation.Configuration;
3135
import org.springframework.hateoas.MediaTypes;
3236
import org.springframework.hateoas.client.LinkDiscoverer;
@@ -37,18 +41,24 @@
3741
import org.springframework.hateoas.server.EntityLinks;
3842
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
3943
import org.springframework.http.MediaType;
44+
import org.springframework.http.codec.CodecConfigurer;
45+
import org.springframework.http.codec.HttpMessageReader;
46+
import org.springframework.http.codec.HttpMessageWriter;
47+
import org.springframework.http.codec.support.DefaultClientCodecConfigurer;
4048
import org.springframework.http.converter.HttpMessageConverter;
49+
import org.springframework.test.util.ReflectionTestUtils;
50+
import org.springframework.web.reactive.function.client.ExchangeStrategies;
51+
import org.springframework.web.reactive.function.client.WebClient;
4152
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
4253

43-
import static org.assertj.core.api.Assertions.assertThat;
44-
4554
/**
4655
* Tests for {@link HypermediaAutoConfiguration}.
4756
*
4857
* @author Roy Clarkson
4958
* @author Oliver Gierke
5059
* @author Andy Wilkinson
5160
* @author Madhura Bhave
61+
* @author Greg Turnquist
5262
*/
5363
class HypermediaAutoConfigurationTests {
5464

@@ -112,6 +122,39 @@ void customizationOfSupportedMediaTypesCanBeDisabled() {
112122
});
113123
}
114124

125+
@Test
126+
void codecsCustomizerShouldRegisterHypermediaTypesWithWebClient() {
127+
this.contextRunner.withUserConfiguration(EnableHypermediaSupportConfig.class,
128+
EnableHypermediaWebClientSupportConfig.class).run((context) -> {
129+
WebClient webClient = context.getBean(WebClient.Builder.class).build();
130+
ExchangeStrategies strategies = (ExchangeStrategies) ReflectionTestUtils
131+
.getField(ReflectionTestUtils.getField(webClient, "exchangeFunction"), "strategies");
132+
133+
assertThat(strategies.messageReaders()).flatExtracting(HttpMessageReader::getReadableMediaTypes)
134+
.contains(MediaTypes.HAL_JSON);
135+
assertThat(strategies.messageWriters()).flatExtracting(HttpMessageWriter::getWritableMediaTypes)
136+
.contains(MediaTypes.HAL_JSON);
137+
});
138+
}
139+
140+
@Test
141+
void codecsCustomizerShouldRegisterHypermediaTypesWithCodecConfigurer() {
142+
this.contextRunner.withUserConfiguration(EnableHypermediaSupportConfig.class,
143+
EnableHypermediaWebClientSupportConfig.class).run((context) -> {
144+
CodecCustomizer customizer = context.getBean(CodecCustomizer.class);
145+
CodecConfigurer configurer = new DefaultClientCodecConfigurer();
146+
customizer.customize(configurer);
147+
CodecConfigurer.CustomCodecs customCodecs = configurer.customCodecs();
148+
149+
assertThat(((Map<HttpMessageReader<?>, Boolean>) ReflectionTestUtils.getField(customCodecs,
150+
"objectReaders")).keySet()).flatExtracting(HttpMessageReader::getReadableMediaTypes)
151+
.containsExactly(MediaTypes.HAL_JSON);
152+
assertThat(((Map<HttpMessageWriter<?>, Boolean>) ReflectionTestUtils.getField(customCodecs,
153+
"objectWriters")).keySet()).flatExtracting(HttpMessageWriter::getWritableMediaTypes)
154+
.containsExactly(MediaTypes.HAL_JSON);
155+
});
156+
}
157+
115158
@ImportAutoConfiguration({ HttpMessageConvertersAutoConfiguration.class, WebMvcAutoConfiguration.class,
116159
JacksonAutoConfiguration.class, HypermediaAutoConfiguration.class })
117160
static class BaseConfig {
@@ -124,4 +167,10 @@ static class EnableHypermediaSupportConfig {
124167

125168
}
126169

170+
@Configuration(proxyBeanMethods = false)
171+
@ImportAutoConfiguration(WebClientAutoConfiguration.class)
172+
static class EnableHypermediaWebClientSupportConfig {
173+
174+
}
175+
127176
}

spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,4 @@ org.springframework.test.context.TestExecutionListener=\
181181
org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener,\
182182
org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener,\
183183
org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener,\
184-
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener
184+
org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener

0 commit comments

Comments
 (0)