千家信息网

Springcloud feign传日期类型参数报错怎么办

发表于:2024-11-19 作者:千家信息网编辑
千家信息网最后更新 2024年11月19日,这篇文章给大家分享的是有关Springcloud feign传日期类型参数报错怎么办的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。feign传日期类型参数报错Date类型参
千家信息网最后更新 2024年11月19日Springcloud feign传日期类型参数报错怎么办

这篇文章给大家分享的是有关Springcloud feign传日期类型参数报错怎么办的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

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调用,那就不报错啦,报错啦。所以使用的时候,一定要注意啊,一定要注意啊。

感谢各位的阅读!关于"Springcloud feign传日期类型参数报错怎么办"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0