Springcloud?feign傳日期類型參數(shù)報錯的解決方案
feign傳日期類型參數(shù)報錯
Date類型參數(shù)報錯
在Spring cloud feign接口中傳遞Date類型參數(shù)時報錯,報錯信息。
場景:
客戶端傳遞一個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類來實現(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實現(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個小時。具體實現(xiàn)沒看到源碼,后續(xù)再研究。需要說明的是,加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();
? ? }這樣注解進(jìn)去的ObjectMapper就帶了時區(qū)。
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沒有提供默認(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傳參問題及傳輸Date類型參數(shù)時差的坑
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客戶端實現(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ù)端實現(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)雅性比第一種好。但個人而言,還是推薦使用第一種。
feign傳參時候使用@DateTimeFormat注解的坑
@NotNull
@MyFuture
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date appointDate; //預(yù)定的預(yù)成班日期 比如這個字段,服務(wù)端上面用了@DateTimeFormat注解,這樣的話,springMVC手機支持直接傳字符串2018-03-03自動轉(zhuǎn)換的。但是,但是,如果你是用client調(diào)用,那就不報錯啦,報錯啦。所以使用的時候,一定要注意啊,一定要注意啊。
小結(jié):雖然fiegn有很多坑,但咱不能說feign不好用。畢竟他比restTemplate或者h(yuǎn)ttpClient還是優(yōu)雅很多的,能夠簡化很多東西,負(fù)載均衡也做得不錯,畢竟在本地就可以做。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- springcloud-feign調(diào)用報錯問題
- 如何解決使用restTemplate進(jìn)行feign調(diào)用new HttpEntity<>報錯問題
- 解決Spring調(diào)用Feign報錯:java.io.IOException:Incomplete output stream問題
- @FeignClient?path屬性路徑前綴帶路徑變量時報錯的解決
- 通過FeignClient調(diào)用微服務(wù)提供的分頁對象IPage報錯的解決
- 使用feign發(fā)送http請求解析報錯的問題
- 解決配置Feign時報錯PathVariable annotation was empty on param 0.
相關(guān)文章
idea項目打開后出現(xiàn)橙色的時鐘圖標(biāo)的解決
本文主要介紹了idea項目打開后出現(xiàn)橙色的時鐘圖標(biāo)的解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
SpringMVC跨服務(wù)器上傳文件中出現(xiàn)405錯誤的解決
這篇文章主要介紹了SpringMVC跨服務(wù)器上傳文件中出現(xiàn)405錯誤的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
Java多線程開發(fā)工具之CompletableFuture的應(yīng)用詳解
做Java編程,難免會遇到多線程的開發(fā),但是JDK8這個CompletableFuture類很多開發(fā)者目前還沒聽說過,但是這個類實在是太好用了,本文就來聊聊它的應(yīng)用吧2023-03-03
Java編程實現(xiàn)向文本文件中讀取數(shù)據(jù)之Scanner用法示例
這篇文章主要介紹了Java編程實現(xiàn)向文本文件中讀取數(shù)據(jù)之Scanner用法,結(jié)合實例形式分析了java使用Scanner類讀取文本文件相關(guān)操作技巧與注意事項,需要的朋友可以參考下2018-03-03

