Closed leon closed 9 years ago
The intention is for Spring Data REST to use the same Jackson configuration as the rest of your app. That's what configureJacksonObjectMapper
in SpringBootRepositoryRestMvcConfiguration
does. It's imported by an auto-configuration class that is configured to be processed after auto-configutration of Jackson has occurred. Unfortunately, when you sub-class SpringBootRepositoryRestMvcConfiguration
it is then processed much earlier and, crucially, before JacksonAutoConfiguration
. This prevents the creation of the auto-configured jacksonObjectMapper
bean as it's @ConditionalOnMissingBean(ObjectMapper.class)
and, by this point, there's already an ObjectMapper
bean in the application context.
A workaround is to use application.properties
rather than a SpringBootRepositoryRestMvcConfiguration
subclass:
spring.data.rest.base-path = /restapi
spring.data.rest.return-body-on-create = true
The non-registration of the JDK 8 module is covered in #2789. The change actually needs to be made in Spring Framework's Jackson2ObjectMapperBuilder
. You may want to raise in issue in the Spring Framework JIRA if @olivergierke has not already done so. /cc @sdeleuze.
@wilkinsona How can i configure the expose for id with a application.yml file?
config.exposeIdsFor(Customer.class, Account.class);
The preferred way would be to be able to specify a list of classes
spring.data.rest:
basePath: /restapi
returnBodyOnCreate: true
exposeIdsFor:
- se.radley.domain.Account
- se.radley.domain.Customer
Since the RepositoryRestConfiguration
doesn't expose a
setExposeIdsFor(List<Class<?>> classes)
The @ConfigurationProperties(prefix = "spring.data.rest")
doesn't work
If we add the setter wouldn't that fix that problem?
I think that'd more be an extension of the workaround, rather than a fix for the underlying problem.
I first tried getting it working with only the application.yml but i got stuck when I got to the exposeIdsFor
I then got the id's working but then the dates stopped working :)
https://jira.spring.io/browse/SPR-12983
I think both scenarios should be supported.
There must be some way to tell the overridden class to wait for the jackson autoconfig?
@Order
?
@Order
won't help here. Auto-configuration classes are processed using a DeferredImportSelector
which is "a variation of ImportSelector
that runs after all @Configuration
beans have been processed". No matter what @Order
you assign to your RestConfig
class, it will be processed before the auto-configuration and will trigger the problem.
I've dug into this a little bit more and the problem is actually caused by the auto-configuration, or not, of a MappingJackson2HttpMessageConverter
.
In the successful case, Spring Boot's HttpMessageConvertersAutoConfiguration
runs before a MappingJackson2HttpMessageConverter
bean exists in the application context. This means that JacksonHttpMessageConvertersConfiguration
creates such a bean that uses an ObjectMapper
configured via the environment. HttpMessageConverters
gives the converter priority over Spring MVC's default JSON converter and it handles the request and serialises the dates correctly.
When a SpringBootRepositoryRestMvcConfiguration
subclass exists it creates two Mapping Jackson2HttpMessageConverter
beans, both of which are actually TypeConstrainedMappingJackson2HttpMessageConverter
, which use an ObjectMapper
configured via the environment. So far so good, however the type-constrained nature of the message converters means that, when HttpMessageConverters
is ordering the converters, the Spring MVC default MappingJackson2HttpMessageConverter
is preferred and it handles the request. It uses an ObjectMapper
in its default configuration rather than the one that's configured via the environment so the dates are serialised as timestamps.
There are (at least) two ways forward:
TypeConstrainedMappingJackson2HttpMessageConverter
, update the message converter auto-configuration such that the presence of type-constrained converters doesn't disable the general purpose converterJackson2ObjectMapperBuilder
to configure the ObjectMapper
of any that use one.The latter option can be implemented as a workaround by declaring this HttpMessageConverters
bean in your application:
@Bean
public HttpMessageConverters httpMessageConverters(
final Jackson2ObjectMapperBuilder builder,
List<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters) {
@Override
protected List<HttpMessageConverter<?>> postProcessConverters(
List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
builder.configure(((MappingJackson2HttpMessageConverter) converter)
.getObjectMapper());
}
}
return converters;
}
};
}
A related problem is that, with the default converters disabled:
@Bean
public HttpMessageConverters httpMessageConverters(List<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(false, converters);
}
a request for json will result in a 406 response as the type-constrained converter has prevented auto-configuration of the general converter and the type-constrained converter will return false from canWrite
. This makes me think that the auto-configuration should be updated, if possible, so that a type-constrained converter doesn't prevent the auto-configuration of the general purpose converter.
I've pushed a fix for this problem here. I haven't merged it as it won't fix the problem until the Spring Data REST change that is referenced above has been made.
The Spring Data fix will be in Spring Data Evans-SR3 so we should be able to pick this up in 1.2.5.
If I overwrite SpringBootRepositoryRestMvcConfiguration to enable some extra conf (simplified in the example below) the jsr-310 / jdk8 jackson modules are not being used
As soon as I remove the RestConfig class the modules are in use and the dates come out right.
I've created a demo repo. https://github.com/leon/temp-spring-boot-data-rest-jackson-problem
Another strange thing is that the JSR-310 module gets autoregistered, but the JDK8Module doesn't so I need to add it explicitly?!
Solution
Can we somehow wait for the jackson auto configuration to complete before creating the rest object mapper, or even better why not use the same object mapper?