Apply HATEOAS module to primary ObjectMapper
Update HypermediaAutoConfiguration to apply the Jackson2HalModule to the primary ObjectMapper. This restores the behavior of Spring Boot 1.1 where HATEOAS types could be serialized for both `application/json` and `application/json+hal` content types. A `spring.hateoas.apply-to-primary-object-mapper` property has also been provided to opt-out if necessary. Fixes gh-2147
This commit is contained in:
parent
ea84479e9a
commit
fd97c7553c
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.hateoas;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties properties} for Spring HATEOAS.
|
||||
*
|
||||
* @author Phillip webb
|
||||
* @since 1.2.1
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.hateoas")
|
||||
public class HateoasProperties {
|
||||
|
||||
/**
|
||||
* If HATEOAS support should be applied to the primary ObjectMapper.
|
||||
*/
|
||||
private boolean applyToPrimaryObjectMapper = true;
|
||||
|
||||
public boolean isApplyToPrimaryObjectMapper() {
|
||||
return this.applyToPrimaryObjectMapper;
|
||||
}
|
||||
|
||||
public void setApplyToPrimaryObjectMapper(boolean applyToPrimaryObjectMapper) {
|
||||
this.applyToPrimaryObjectMapper = applyToPrimaryObjectMapper;
|
||||
}
|
||||
|
||||
}
|
|
@ -16,8 +16,14 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.hateoas;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
|
@ -27,14 +33,18 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
|||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.EntityLinks;
|
||||
import org.springframework.hateoas.LinkDiscoverers;
|
||||
import org.springframework.hateoas.RelProvider;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.config.EnableEntityLinks;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.hal.CurieProvider;
|
||||
import org.springframework.hateoas.hal.Jackson2HalModule;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.plugin.core.Plugin;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -55,6 +65,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
@ConditionalOnWebApplication
|
||||
@AutoConfigureAfter({ WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class })
|
||||
@EnableConfigurationProperties(HateoasProperties.class)
|
||||
public class HypermediaAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
|
@ -65,40 +76,90 @@ public class HypermediaAutoConfiguration {
|
|||
@ConditionalOnClass({ Jackson2ObjectMapperBuilder.class, ObjectMapper.class })
|
||||
protected static class HalObjectMapperConfiguration {
|
||||
|
||||
@Autowired
|
||||
private HateoasProperties hateoasProperties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Jackson2ObjectMapperBuilder objectMapperBuilder;
|
||||
private CurieProvider curieProvider;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("_relProvider")
|
||||
private RelProvider relProvider;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ObjectMapper primaryObjectMapper;
|
||||
|
||||
@PostConstruct
|
||||
public void configurePrimaryObjectMapper() {
|
||||
if (this.primaryObjectMapper != null
|
||||
&& this.hateoasProperties.isApplyToPrimaryObjectMapper()) {
|
||||
registerHalModule(this.primaryObjectMapper);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerHalModule(ObjectMapper objectMapper) {
|
||||
objectMapper.registerModule(new Jackson2HalModule());
|
||||
Jackson2HalModule.HalHandlerInstantiator instantiator = new Jackson2HalModule.HalHandlerInstantiator(
|
||||
HalObjectMapperConfiguration.this.relProvider,
|
||||
HalObjectMapperConfiguration.this.curieProvider);
|
||||
objectMapper.setHandlerInstantiator(instantiator);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BeanPostProcessor halObjectMapperConfigurer() {
|
||||
return new BeanPostProcessor() {
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean,
|
||||
String beanName) throws BeansException {
|
||||
if (HalObjectMapperConfiguration.this.objectMapperBuilder != null
|
||||
&& bean instanceof ObjectMapper
|
||||
&& "_halObjectMapper".equals(beanName)) {
|
||||
HalObjectMapperConfiguration.this.objectMapperBuilder
|
||||
.configure((ObjectMapper) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean,
|
||||
String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
};
|
||||
public static HalObjectMapperConfigurer halObjectMapperConfigurer() {
|
||||
return new HalObjectMapperConfigurer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(EntityLinks.class)
|
||||
@EnableEntityLinks
|
||||
protected static class EntityLinksConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to apply any {@link Jackson2ObjectMapperBuilder}
|
||||
* configuration to the HAL {@link ObjectMapper}.
|
||||
*/
|
||||
private static class HalObjectMapperConfigurer implements BeanPostProcessor,
|
||||
BeanFactoryAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof ObjectMapper && "_halObjectMapper".equals(beanName)) {
|
||||
postProcessHalObjectMapper((ObjectMapper) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void postProcessHalObjectMapper(ObjectMapper objectMapper) {
|
||||
try {
|
||||
Jackson2ObjectMapperBuilder builder = this.beanFactory
|
||||
.getBean(Jackson2ObjectMapperBuilder.class);
|
||||
builder.configure(objectMapper);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// No Jackson configuration required
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,19 +75,17 @@ public class HypermediaAutoConfigurationTests {
|
|||
|
||||
@Test
|
||||
public void doesBackOffIfEnableHypermediaSupportIsDeclaredManually() {
|
||||
|
||||
this.context = new AnnotationConfigWebApplicationContext();
|
||||
this.context.register(SampleConfig.class, HypermediaAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
|
||||
this.context.getBean(LinkDiscoverers.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jacksonConfigurationIsAppliedToTheHalObjectMapper() {
|
||||
this.context = new AnnotationConfigWebApplicationContext();
|
||||
this.context.register(HypermediaAutoConfiguration.class,
|
||||
JacksonAutoConfiguration.class);
|
||||
this.context.register(JacksonAutoConfiguration.class,
|
||||
HypermediaAutoConfiguration.class);
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.jackson.serialization.INDENT_OUTPUT:true");
|
||||
this.context.refresh();
|
||||
|
|
|
@ -96,6 +96,9 @@ content into your application; rather pick only the properties that you need.
|
|||
spring.resources.cache-period= # cache timeouts in headers sent to browser
|
||||
spring.resources.add-mappings=true # if default mappings should be added
|
||||
|
||||
# SPRING HATEOS ({sc-spring-boot-autoconfigure}/hateoas/HateoasProperties.{sc-ext}[HateoasProperties])
|
||||
spring.hateoas.apply-to-primary-object-mapper=true # if the primary mapper should also be configured
|
||||
|
||||
# HTTP encoding ({sc-spring-boot-autoconfigure}/web/HttpEncodingProperties.{sc-ext}[HttpEncodingProperties])
|
||||
spring.http.encoding.charset=UTF-8 # the encoding of HTTP requests/responses
|
||||
spring.http.encoding.enabled=true # enable http encoding support
|
||||
|
|
Loading…
Reference in New Issue