这几天的开发过程中,需要修改原有同事写的项目,原项目采用的是springboot写的后端服务,json序列化使用原始jackson进行,并在配置文件的properties文件中声明了jackson的一些基本配置
# Json spring.jackson.time-zone=GMT+8 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.property-naming-strategy=SNAKE_CASE spring.jackson.deserialization.READ_UNKNOWN_ENUM_VALUES_AS_NULL=true
起初在没有加入项目的拦截器LoginInterceptor的时候,按照项目配置文件的配置,postman请求时采用属性为下划线的形式并不会报错,而且在序列化时间类型的属性时也不会出现时间格式化出问题的情况。开始对项目进行改造之后增加了登陆验证。并在springboot项目启动的时候增加了Webconfig来向程序注册拦截器
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
// 关键,将拦截器作为bean写入配置中
@Autowired private LoginInterceptor loginInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); // 上传图片的路径除外
super.addInterceptors(registry);
}
}
做完这些之后,准备调试程序时发现,用原有的报文请求程序的接口的时候总是报错。首先是时间类型转换失败,报错信息如下:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize value of type java.util.Date from String "2018-06-12 12:00:00": not a valid representation (error: Failed to parse Date value '2018-06-12 12:00:00': Can not parse date "2018-06-12 12:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.util.Date from String "2018-06-12 12:00:00": not a valid representation (error: Failed to parse Date value '2018-06-12 12:00:00': Can not parse date "2018-06-12 12:00:00": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS', parsing fails (leniency? null)) at [Source: java.io.PushbackInputStream@7688ebdb; line: 1, column: 281] (through reference chain: com.hlt.cloudoak.base.ForeignApplication["order"]->com.hlt.cloudoak.base.ForeignApplication$Order["notification"]->com.hlt.cloudoak.base.ForeignApplication$Order$Notification["date"]->com.hlt.cloudoak.base.ForeignApplication$Order$Notification$HappenDate["notify"])
然后通过一通查资料,最终在Model的属性上增加了@JsonFormat注解得以解决了这个时间格式化错误的问题。
但是事情并没有完,虽然解决了时间格式化报错的问题。但是使用下划线形式的Json请求接口依旧行不通,转化失败。程序拿不到对应属性的值。于是又是一顿某度和某歌的翻找。
最后在WebConfig类的实现中增加了代码,才使得原有的项目依旧得以采用下划线形式的Json,并且时间格式化时也并不会出错,最终代码如下:
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
// 关键,将拦截器作为bean写入配置中
@Autowired private LoginInterceptor loginInterceptor;
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); // 上传图片的路径除外
super.addInterceptors(registry);
}
@Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(customJackson2HttpMessageConverter());
super.addDefaultHttpMessageConverters(converters);
}
}
资料上讲,自定义WebMvcConfigur之后,原有properties中的jackson配置会失效。所以必须在自定义实现类中再次对jackson的配置进行补充。查询资料的过程中,看到有的文章提到需要将注解@EnableWebMvc去掉。但是我们的项目中并不显式的包含这个注解,相信可能有部分人跟我一样在看到这个解决方案时并不知道如何对项目进行更改。下面贴一下官方的一段话来解释一下WebMvcConfigurationSupport类:
public class WebMvcConfigurationSupport
extends Object
implements ApplicationContextAware, ServletContextAware
@EnableWebMvc to an application@Configuration class. An alternative more advanced option is to extend directly from this class and override methods as necessary remembering to add@Configuration to the subclass and @Bean to overridden @Bean methods. For more details see the Javadoc of @EnableWebMvc.This class registers the following HandlerMappings:
RequestMappingHandlerMappingordered at 0 for mapping requests to annotated controller methods.HandlerMappingordered at 1 to map URL paths directly to view names.BeanNameUrlHandlerMappingordered at 2 to map URL paths to controller bean names.HandlerMappingordered atInteger.MAX_VALUE-1to serve static resource requests.HandlerMappingordered atInteger.MAX_VALUEto forward requests to the default servlet.
Registers these HandlerAdapters:
RequestMappingHandlerAdapterfor processing requests with annotated controller methods.HttpRequestHandlerAdapterfor processing requests withHttpRequestHandlers.SimpleControllerHandlerAdapterfor processing requests with interface-basedControllers.
Registers a HandlerExceptionResolverComposite with this chain of exception resolvers:
ExceptionHandlerExceptionResolverfor handling exceptions through @ExceptionHandlermethods.ResponseStatusExceptionResolverfor exceptions annotated with @ResponseStatus.DefaultHandlerExceptionResolverfor resolving known Spring exception types
Registers an AntPathMatcher and a UrlPathHelper to be used by:
- the
RequestMappingHandlerMapping, - the
HandlerMappingfor ViewControllers - and the
HandlerMappingfor serving resources
Note that those beans can be configured with a PathMatchConfigurer.
Both the RequestMappingHandlerAdapter and the ExceptionHandlerExceptionResolver are configured with default instances of the following by default:
- a
ContentNegotiationManager - a
DefaultFormattingConversionService - a
OptionalValidatorFactoryBeanif a JSR-303 implementation is available on the classpath - a range of
HttpMessageConverters depending on the third-party libraries available on the classpath.