I filed this issue at Spring first and they redirected me here. But I'm expecting you guys to rebuff me as well and argue this issue is caused by some bad Spring cloud integration...
I'm using Spring boot 1.3.6, Spring Cloud Brixton.SR3, Java 1.8. It is no longer possible to use a Feign client accepting a java.time.LocalDate as a method param where it is supposed to comply with a given format like @DateTimeFormat(iso = ISO.DATE).
If you do, you get a java.time.format.DateTimeParseException: Text '7/11/16' could not be parsed at index 0", where it's clear the serialization format is NOT the requested ISO-8601 uuuu-MM-dd but M/d/uu. I put the whole stacktrace below.
I made a stripped project to reproduce the issue: https://github.com/fabmars/feign-localdate-bug Just import, run ApplicationServer, ApplicationClient, they are supposed to query a local eureka server, and hit http://localhost:8080/bug
It's worth noticing that LocalDate's used to be serialized correctly back in spring-cloud-netflix:1.0.7, you may even try it in my example project. So there is a strong suspiscion there is a regression since spring-cloud-netflix:1.1.0 at least (before Brixton, even).
2016-07-11 21:48:32.838 ERROR 9398 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.netflix.hystrix.exception.HystrixRuntimeException: getTomorrow failed and no fallback available.] with root cause
feign.FeignException: status 400 reading TimeClient#getTomorrow(LocalDate,String); content:
{"timestamp":1468266512745,"status":400,"error":"Bad Request","exception":"org.springframework.web.method.annotation.MethodArgumentTypeMismatchException","message":"Failed to convert value of type [java.lang.String] to required type [java.time.LocalDate]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam @org.springframework.format.annotation.DateTimeFormat java.time.LocalDate] for value '7/11/16'; nested exception is java.time.format.DateTimeParseException: Text '7/11/16' could not be parsed at index 0","path":"/tomorrow"}
at feign.FeignException.errorStatus(FeignException.java:62) ~[feign-core-8.16.2.jar:8.16.2]
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:91) ~[feign-core-8.16.2.jar:8.16.2]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:134) ~[feign-core-8.16.2.jar:8.16.2]
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) ~[feign-core-8.16.2.jar:8.16.2]
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:97) ~[feign-hystrix-8.16.2.jar:8.16.2]
at com.netflix.hystrix.HystrixCommand$1.call(HystrixCommand.java:293) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.HystrixCommand$1.call(HystrixCommand.java:289) ~[hystrix-core-1.5.3.jar:1.5.3]
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.1.5.jar:1.1.5]
at rx.Observable.unsafeSubscribe(Observable.java:8460) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.1.5.jar:1.1.5]
at rx.Observable.unsafeSubscribe(Observable.java:8460) ~[rxjava-1.1.5.jar:1.1.5]
at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94) ~[rxjava-1.1.5.jar:1.1.5]
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47) ~[hystrix-core-1.5.3.jar:1.5.3]
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69) ~[hystrix-core-1.5.3.jar:1.5.3]
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) ~[rxjava-1.1.5.jar:1.1.5]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_72]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_72]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[na:1.8.0_72]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_72]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_72]
Comment From: uguy
It look s like when the Feign expander is called, it has no info on the @DateTimeFormat annotation (or any other ?) so no ways to apply custom conversion ?
ReflectiveFeign, line 205 :
value = indexToExpander.get(i).expand(value);
Then Spring Expander call the conversion service to get a String representation of the value.
Applying ISO DATE format to all feign cliens as default is workaround to format date but not a fix :
@Bean
public FeignFormatterRegistrar localDateFeignFormatterRegistrar() {
return new FeignFormatterRegistrar() {
@Override
public void registerFormatters(FormatterRegistry formatterRegistry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(formatterRegistry);
}
};
}
Comment From: michaeltecourt
I confirm, the @DateTimeFormat annotation is completely ignored, no matter the type used : LocalDate, OffsetDateTime, LocalDateTime...
@uguy's workaround (thank you BTW) can also be used with JodaTime using org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar instead of org.springframework.format.datetime.standard.DateTimeFormatterRegistrar.
Both tools can be initialized in the registerFormatters method if needed.
Comment From: spencergibb
@fabmars can you put the spring issue link here?
@uguy @michaeltecourt how do you think a solution from us might behave?
Comment From: michaeltecourt
@spencergibb it would be cool if the client side behavior of @DateTimeFormat matched the server side :
@FeignClient(name = "data-api", url = "http://example.com")
public interface DataApiClient {
@RequestMapping(value = "/api/data", method = RequestMethod.GET)
Data getData(@DateTimeFormat(iso = ISO.DATE_TIME) @RequestParam("from") OffsetDateTime from,
@DateTimeFormat(pattern = "yyyy-MM") @RequestParam("to") OffsetDateTime to);
}
I would expect the example above to create the following URL :
http://example.com/api/data?from=2016-09-15T00:00:00.000Z&to=2016-09
Comment From: simasch
Run into the same problem today. Hope this will be fixed soon
Comment From: uguy
@spencergibb Same for me. The closer the client contract expressed is from the server contract, the better. But I do not know how to do this right now (need to look a bit further in the code).
It could be linked to another issue which seems to be also related to annotation parsing/handling : https://github.com/spring-cloud/spring-cloud-netflix/issues/867
Maybe a generic solution could be found ?
Comment From: fabmars
Sorry for not responding earlier. @spencergibb that was https://jira.spring.io/browse/DATAREST-851 @uguy 100% agreed
Comment From: m190
Are there any way how to make custom date format now?
I tried to use the list of date and neither approach is working. I have such a client
@FeignClient(name = "exampleClient", url = "#{EXAMPLE_URL}" )
public interface ExampleClient{
@RequestMapping(value = "/example", method = RequestMethod.GET)
String getExampleValue(@RequestParam(name = "date") List<Date> dates);
}
And I couldn't find any way how to format the date. I think it's partially because of the https://github.com/spring-cloud/spring-cloud-netflix/issues/1115 fix. The extender is not appends, and no formatter is used,
Comment From: innokenty
Does the workaround with the registrar posted above work for anyone? I still bget this annotation totally ignored. I'm using Joda LocaDate, but switching to Joda registrar doesn't change anything. Is there something else I need to add maybe?
Comment From: fabmars
Yes it does! Working here on the latest SpringBoot 1.4.2
Comment From: igorbljahhin
Same issue here. Feign version is 9.3.1, Spring Cloud 1.2.6
Feign client is following:
@FeignClient(name = "pricing-service", url = "${modules.pricing.url}")
public interface SailPricesResource {
@RequestMapping(value = "/api/v1/prices/{date}/{time}", method = RequestMethod.GET)
GetSailPricesResponseDTO getSailPricesForDeparture(final @PathVariable("date") LocalDate date, final @PathVariable("time") LocalTime time);
}
The date gets formatted as "D/M/YY" and time gets formatted as "hh:mm a", although there are custom converters defined in
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new LocalDateToStringConverter());
registry.addConverter(new StringToLocalDateConverter());
registry.addConverter(new LocalTimeToStringConverter());
registry.addConverter(new StringToLocalTimeConverter());
registry.addConverter(new LocalDateTimeToStringConverter());
registry.addConverter(new StringToLocalDateTimeConverter());
}
}
After debugging I discovered that Feigh clients are created before the conversion service is updated with my custom converters. The issue has gone only after I added
@Configuration
public class FeignConfiguration {
@Bean
public Contract feignContract() {
return new SpringMvcContract();
}
}
Comment From: ghost
+1
Comment From: MrSummer33
@spencergibb can i put my localDate field in ResquestBody?
Comment From: willpewitt
Any resolution on this?
Comment From: ryanjbaxter
If there was it would have been closed
Comment From: CH-jakegale
+1 on finding this issue. Expander is ignoring DateTime formats. Adding @uguy 's bean fixes the issue.
Comment From: Mintas
Hello, everybody! Looks, like you are missing one of this ultrauseful thing, provided mostly by spring: 1) org.springframework.cloud.openfeign.FeignClientsConfiguration.class - will autoconfigure decoders/encoders with lookup to other beans for serialization/deserialization 2) org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration - will autoconfigure jackson/gson/json message converters, used with spring boot anf feign internally 3) com.fasterxml.jackson.datatype:jackson-datatype-jsr310 - be sure you have properly included this library dependency to your classpath. It gives you JavaTimeModule for ObjectMapper, without this feature all your solutions will not work.
Comment From: fabmars
And do you have a solution to propose that's better than @uguy 's ?
Comment From: Mintas
You know, that solution is really very very outdated, and all this features are autoconfigured this days, anyway, you can combine both. ( you can inspect sources of classes mentioned above and find out am telling the truth) Just have to note, that @uguy solution didn't help at our case at all =( https://github.com/spring-cloud/spring-cloud-openfeign/issues/104#issuecomment-232330995 And one more thing: it will not work without JSR310 dependency.
BTW, this is good, because it shows you, how to add additional configurations or how to proceed if you do not use autoconfigurations at all... but who doesnt? =)
UPD: we use
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:2.1.1.RELEASE"
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Greenwich.M3"
}
}
Comment From: rafaelfbs
https://github.com/spring-cloud/spring-cloud-openfeign/issues/47
Comment From: OlgaMaciaszek
Fixed with https://github.com/spring-cloud/spring-cloud-openfeign/issues/47.