Springcloud feign传日期类型参数报错的解决方案

网友投稿 719 2022-08-20


Springcloud feign传日期类型参数报错的解决方案

目录feign传日期类型参数报错Date类型参数报错LocalDate类型报错feign传参问题及传输Date类型参数时差的坑下面说说两种解决方案feign传参时候使用@DateTimeFormat注解的坑

feign传日期类型参数报错

Date类型参数报错

在Spring cloud feign接口中传递Date类型参数时报错,报错信息。

场景:

客户端传递一个new Date()的参数,服务端接受的参数和客户端有时间差。

客户端打印格式化的new Date():

2018-05-11 10:23:36

而服务端接收到的参数是:

2018-05-12 00:23:36

我们从Feign启动的源码可以看出,Feign在encode和decode时会用SpringEncoder类来实现:

@Bean

@ConditionalOnMissingBean

public Decoder feignDecoder() {

return new ResponseEntityDecoder(new SpringDecoder(this.messageConverters));

}

@Bean

@ConditionalOnMissingBean

public Encoder feignEncoder() {

return new SpringEncoder(this.messageConverters);

}

而SpringEncoder的HttpMessageConverters使用的是Jackson默认模板,该模板来自基类WebMvcConfigurationSupport.java:

protected final List> getMessageConverters() {

if (this.messageConverters == null) {

this.messageConverters = new ArrayList>();

configureMessageConverters(this.messageConverters);

if (this.messageConverters.isEmpty()) {

addDefaultHttpMessageConverters(this.messageConverters);

}

extendMessageConverters(this.messageConverters);

}

return this.messageConverters;

}

而WebMvcConfigurationSupport.java最终使用的是默认的ObjectMapper生成的MappingJackson2HttpMessageConverter。

至此可以看出该问题的产生并不是Feign的问题,而是Feign实现中使用的Spring MVC中的Jackson转换参数问题,默认的TimeZone并不是东八区,而是UTC。

/**

* Override the default {@link TimeZone} to use for formatting.

* Default value used is UTC (NOT local timezone).

* @since 4.1.5

*/

public Jackson2ObjectMapperBuilder timeZone(TimeZone timeZone) {

this.timeZone = timeZone;

return this;

}

这个问题,在Spring MVC中可以在接口或者字段上添加注解来解决,但在Feign中使用GET请求的接口添加注解是不行的。debug发现,Spring MVC在处理Date的时候,调用了sun.reflect.ConstructorAccessor#newInstance(Object[] var1),时间会加14个小时。具体实现没看到源码,后续再研究。需要说明的是,加jsonFormat注解对于Feign接口没生效,但Spring MVC是可以的。

OK,回到正题。要解决这个问题,最好的办法是自定义ObjectMapper。即使是加了注解可以解决问题,也依然推荐使用自定义ObjectMapper,因为大量的接口每个都添加注解太繁琐了。

@Bean

@Primary

public ObjectMapper objectMapper() {

return Jackson2ObjectMapperBuilder.json()

.serializationInclusion(JsonInclude.Include.NON_NULL)

.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)

.timeZone(TimeZone.getTimeZone("Asia/Shanghai"))

.build();

}

这样注解进去的ObjectMapper就带了时区。

LocalDate类型报错

报错详情:

Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not construct instance of java.time.LocalDate: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDate: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)at [Source: java.io.PushbackInputStream@3ce2b1e2; line: 1, column: 44] (through reference chain: com.chunrun.user.param.UserParams["localDate"])

这是因为LocalDate没有提供默认的构造器,而Jackson还不支持Java8的特征。这时候只需要加上依赖,ObjectMapper加一行代码即可:

com.fasterxml.jackson.datatype

jackson-datatype-jsr310

2.4.0

@Bean

@Primary

public ObjectMapper objectMapper() {

return Jackson2ObjectMapperBuilder.json()

.serializationInclusion(JsonInclude.Include.NON_NULL)

.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)

.timeZone(TimeZone.getTimeZone("Asia/Shanghai"))

.modules(new JSR310Module())

.build();

}

以上配置调用方也需要。

feign传参问题及传输Date类型参数时差的坑

feign的调用如下:

List getLeftSeatCountOfDays(

@RequestParam("configType") Integer configType,

@RequestParam("courseId") Long courseId,

@RequestParam("startDateFrom") Date startDateFrom,

@RequestParam("startDateTo") Date startDateTo,

@RequestParam("level") Integer level);

我们采用了两个date类型的参数传参,结果:

我们传入的时间为:

但服务端接受到的时间为:

天啊撸,竟然出现了我们并不熟悉的14h时差,并不是我们熟悉的8个小时。feign真是天坑啊。这是SpringCloud Feign传Date类型参数的时差导致的。

备注:使用date类型传参,如果是body里面用对象传,是不会出现时差问题的。

下面说说两种解决方案

当发送时间类型时,直接用String发送(推荐)Feign客户端实现FeignFormatterRegistrar接口自定义DateFormatRegister

@Component

public class DateFormatRegister implements FeignFormatterRegistrar{

public DateFormatRegister(){

}

@Override

public void registerFormatters(FormatterRegistry registry) {

registry.addConverter(Date.class, String.class, new Date2StringConverter());

}

private class Date2StringConverter implements Converter{

@Override

public String convert(Date source) {

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

return sdf.format(source);

}

}

}

服务端实现:

@Configuration

public class WebConfigBeans {

@Autowired

private RequestMappingHandlerAdapter handlerAdapter;

/**

* 增加字符串转日期的功能

*/

@PostConstruct

public void initEditableValidation() {

ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) handlerAdapter

.getWebBindingInitializer();

if (initializer.getConversionService() != null) {

GenericConversionService genericConversionService = (GenericConversionService) initializer

.getConversionService();

genericConversionService.addConverter(String.class, Date.class, new String2DateConverter());

}

}

}

第二种比较麻烦,但是一劳永逸,代码的优雅性比第一种好。但个人而言,还是推荐使用第一种。

feign传参时候使用@DateTimeFormat注解的坑

@NotNull

@MyFuture

@DateTimeFormat(pattern = "yyyy-MM-dd")

private Date appointDate; //预定的预成班日期

比如这个字段,服务端上面用了@DateTimeFormat注解,这样的话,springMVC手机支持直接传字符串2018-03-03自动转换的。但是,但是,如果你是用client调用,那就不报错啦,报错啦。所以使用的时候,一定要注意啊,一定要注意啊。

小结:虽然fiegn有很多坑,但咱不能说feign不好用。毕竟他比restTemplate或者httpClient还是优雅很多的,能够简化很多东西,负载均衡也做得不错,毕竟在本地就可以做。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring连接Mysql数据库的实现步骤
下一篇:Java的Comparable,Comparator和Cloneable三大接口详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~