您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關(guān)Springcloud feign傳日期類型參數(shù)報(bào)錯怎么辦的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
在Spring cloud feign接口中傳遞Date類型參數(shù)時報(bào)錯,報(bào)錯信息。
場景:
客戶端傳遞一個new Date()的參數(shù),服務(wù)端接受的參數(shù)和客戶端有時間差。
客戶端打印格式化的new Date():
2018-05-11 10:23:36
而服務(wù)端接收到的參數(shù)是:
2018-05-12 00:23:36
我們從Feign啟動的源碼可以看出,F(xiàn)eign在encode和decode時會用SpringEncoder類來實(shí)現(xiàn):
@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默認(rèn)模板,該模板來自基類WebMvcConfigurationSupport.java:
protected final List<HttpMessageConverter<?>> getMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList<HttpMessageConverter<?>>(); configureMessageConverters(this.messageConverters); if (this.messageConverters.isEmpty()) { addDefaultHttpMessageConverters(this.messageConverters); } extendMessageConverters(this.messageConverters); } return this.messageConverters; }
而WebMvcConfigurationSupport.java最終使用的是默認(rèn)的ObjectMapper生成的MappingJackson2HttpMessageConverter。
至此可以看出該問題的產(chǎn)生并不是Feign的問題,而是Feign實(shí)現(xiàn)中使用的Spring MVC中的Jackson轉(zhuǎn)換參數(shù)問題,默認(rèn)的TimeZone并不是東八區(qū),而是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發(fā)現(xiàn),Spring MVC在處理Date的時候,調(diào)用了sun.reflect.ConstructorAccessor#newInstance(Object[] var1),時間會加14個小時。具體實(shí)現(xiàn)沒看到源碼,后續(xù)再研究。需要說明的是,加JsonFormat注解對于Feign接口沒生效,但Spring MVC是可以的。
OK,回到正題。要解決這個問題,最好的辦法是自定義ObjectMapper。即使是加了注解可以解決問題,也依然推薦使用自定義ObjectMapper,因?yàn)榇罅康慕涌诿總€都添加注解太繁瑣了。
@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(); }
這樣注解進(jìn)去的ObjectMapper就帶了時區(qū)。
報(bào)錯詳情:
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"])
這是因?yàn)長ocalDate沒有提供默認(rèn)的構(gòu)造器,而Jackson還不支持Java8的特征。這時候只需要加上依賴,ObjectMapper加一行代碼即可:
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.4.0</version> </dependency>
@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(); }
以上配置調(diào)用方也需要。
feign的調(diào)用如下:
List<LeftSeatCountOfDaysResp> getLeftSeatCountOfDays( @RequestParam("configType") Integer configType, @RequestParam("courseId") Long courseId, @RequestParam("startDateFrom") Date startDateFrom, @RequestParam("startDateTo") Date startDateTo, @RequestParam("level") Integer level);
我們采用了兩個date類型的參數(shù)傳參,結(jié)果:
我們傳入的時間為:
但服務(wù)端接受到的時間為:
天啊擼,竟然出現(xiàn)了我們并不熟悉的14h時差,并不是我們熟悉的8個小時。feign真是天坑啊。這是SpringCloud Feign傳Date類型參數(shù)的時差導(dǎo)致的。
備注:使用date類型傳參,如果是body里面用對象傳,是不會出現(xiàn)時差問題的。
當(dāng)發(fā)送時間類型時,直接用String發(fā)送(推薦)
Feign客戶端實(shí)現(xiàn)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<Date,String>{ @Override public String convert(Date source) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(source); } } }
服務(wù)端實(shí)現(xiàn):
@Configuration public class WebConfigBeans { @Autowired private RequestMappingHandlerAdapter handlerAdapter; /** * 增加字符串轉(zhuǎn)日期的功能 */ @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()); } } }
第二種比較麻煩,但是一勞永逸,代碼的優(yōu)雅性比第一種好。但個人而言,還是推薦使用第一種。
@NotNull @MyFuture @DateTimeFormat(pattern = "yyyy-MM-dd") private Date appointDate; //預(yù)定的預(yù)成班日期
比如這個字段,服務(wù)端上面用了@DateTimeFormat注解,這樣的話,springMVC手機(jī)支持直接傳字符串2018-03-03自動轉(zhuǎn)換的。但是,但是,如果你是用client調(diào)用,那就不報(bào)錯啦,報(bào)錯啦。所以使用的時候,一定要注意啊,一定要注意啊。
感謝各位的閱讀!關(guān)于“Springcloud feign傳日期類型參數(shù)報(bào)錯怎么辦”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。