How to configure MappingJackson2HttpMessageConverter registered by spring-hateoas

André Menneken picture André Menneken · Jul 11, 2014 · Viewed 8.8k times · Source

I like to use spring-hateoas in my project and configured it with @EnableHypermediaSupport. The problem now is, that this magic config annotation registers its own MappingJackson2HttpMessageConverter and my own customized converter will be ignored.

Background: I added some Jackson modules (like the JodaModule) to my project and I want them to get registered using objectMapper.findAndRegisterModules();. This is done by overriding configureMessageConverters(List<HttpMessageConverter<?>> converters) in WebMvcConfigurationSupport or WebMvcConfigurer.

My current configuration looks like this:

@Configuration
@EnableHypermediaSupport(type = HAL)
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

  @Override
  protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.getObjectMapper().findAndRegisterModules();

    converters.add(converter);
  }
}

Is there a way to customize the MappingJackson2HttpMessageConverter or the ObjectMapper that is used by spring-hateoas?

Answer

Chris DaMour picture Chris DaMour · Nov 5, 2014

I had to do the same thing. With HATEOAS .16 i was able to ge this working..but really really ugly.

The key was that in the HypermediaSupportBeanDefinitionRegistrar the part that registers the HAL converter checks to see if there already is a HAL converter before it tries to add another one. So I just added a HAL Converter in my WebMVCConfig::configureMessageConverters myself.

Something like:

private static final String DELEGATING_REL_PROVIDER_BEAN_NAME = "_relProvider";
private static final String LINK_DISCOVERER_REGISTRY_BEAN_NAME = "_linkDiscovererRegistry";
private static final String HAL_OBJECT_MAPPER_BEAN_NAME = "_halObjectMapper";

@Autowired
private ListableBeanFactory beanFactory;

private static CurieProvider getCurieProvider(BeanFactory factory) {

    try {
        return factory.getBean(CurieProvider.class);
    } catch (NoSuchBeanDefinitionException e) {
        return null;
    }
}

@Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        List<HttpMessageConverter<?>> baseConverters = new ArrayList<HttpMessageConverter<?>>();
        super.configureMessageConverters(baseConverters);

        //Need to override some behaviour in the HAL Serializer...so let's make our own
        CurieProvider curieProvider = getCurieProvider(beanFactory);
        RelProvider relProvider = beanFactory.getBean(DELEGATING_REL_PROVIDER_BEAN_NAME, RelProvider.class);
        ObjectMapper halObjectMapper = beanFactory.getBean(HAL_OBJECT_MAPPER_BEAN_NAME, ObjectMapper.class);

        halObjectMapper.registerModule(new Jackson2HalModule());
        halObjectMapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, curieProvider));

        MappingJackson2HttpMessageConverter halConverter = new TypeConstrainedMappingJackson2HttpMessageConverter(
            ResourceSupport.class);
        halConverter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
        halConverter.setObjectMapper(halObjectMapper);

        converters.add(halConverter);
    }

This is clearly implementation dependent, uses implementation details, and really doesn't let you modify the one that @EnableHyperMediaSupport build for you....but it works for now.