【问题标题】:spring-cloud-feign Client and @RequestParam with Date typespring-cloud-feign 客户端和 @RequestParam 与 Date 类型
【发布时间】:2016-05-11 12:42:19
【问题描述】:

这次我在使用声明式 REST 客户端,在一些 Spring Boot 应用程序中进行 Feign。

我想要实现的是调用我的 REST API 之一,如下所示:

@RequestMapping(value = "/customerslastvisit", method = RequestMethod.GET)
    public ResponseEntity customersLastVisit(
            @RequestParam(value = "from", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date from,
            @RequestParam(value = "to", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date to) {

如您所见,API 接受使用 from 和 to 日期参数格式的调用,如 (yyyy-MM-dd)

为了调用该 API,我准备了以下@FeignClient

@FeignClient("MIIA-A")
public interface InboundACustomersClient {
    @RequestMapping(method = RequestMethod.GET, value = "/customerslastvisit")
    ResponseEntity customersLastVisit(
            @RequestParam(value = "from", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date from,
            @RequestParam(value = "to", required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) Date to);
}

一般来说,几乎是复制粘贴。现在在我的启动应用程序的某个地方,我使用它:

DateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
ResponseEntity response = inboundACustomersClient.customersLastVisit(formatter.parse(formatter.format(from)),
        formatter.parse(formatter.format(to)));

而且,我得到的是

嵌套异常是 org.springframework.core.convert.ConversionFailedException: 失败 从类型 [java.lang.String] 转换为类型 [@org.springframework.web.bind.annotation.RequestParam @org.springframework.format.annotation.DateTimeFormat java.util.Date] 价值 'Sun May 03 00:00:00 CEST 2015';

嵌套异常是 java.lang.IllegalArgumentException: Unable to 解析“2015 年 5 月 3 日星期日 00:00:00 CEST”

所以,问题是,我对请求做错了什么,它在发送到我的 API 之前没有解析为“仅限日期”格式?或者可能是纯粹的 Feign lib 问题?

【问题讨论】:

    标签: java spring rest jackson netflix-feign


    【解决方案1】:

    feign 客户端现在(2020 年 12 月)使用上面原始问题中的语法和 java.time.LocalDate 作为参数可以正常工作。也就是说,您可以使用:

      @FeignClient(name = "YOUR-SERVICE")
      interface ClientSpec {
        @RequestMapping(value = "/api/something", method = RequestMethod.GET)
        String doSomething(
            @RequestParam("startDate") 
            @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) 
            LocalDate startDate);
      }
    

    【讨论】:

      【解决方案2】:

      您可以只使用扩展器对象。

       /**
       * @see com.x.y.z.rest.configuration.SomeController#search(Integer, Integer, Date, Date)
       */
      @RequestLine("GET /configuration/{customerId}?&startDate={startDate}")
      Set<AnObject> searchSomething(@Param("customerId") Integer customerId, @Param(value = "startDate", expander = FeignDateExpander.class) Date startDate);
      
      
      public class FeignDateExpander implements Param.Expander {
      
      @Override
      public String expand(Object value) {
          Date date = (Date)value;
          return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
      }
      }
      

      【讨论】:

        【解决方案3】:

        这是一个线程安全的解决方案,不会在您的 spring 世界中注册默认日期格式化程序。但请记住,此格式化程序将作为新的默认值用于所有 feign 客户端,并且您实际上应该使用 joda 日期时间或新的 java 日期时间:

        @Configuration
        public class FeignFormatterRegister implements FeignFormatterRegistrar {
        
            @Override
            public void registerFormatters(FormatterRegistry registry) {
                registry.addFormatter(new DateFormatter());
            }
        
            /*
             * SimpleDateFormat is not thread safe!
             * consider using joda time or java8 time instead
             */
            private static class DateFormatter implements Formatter<Date> {
                static final ThreadLocal<SimpleDateFormat> FORMAT = ThreadLocal.withInitial(
                        () -> new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
                );
        
                @Override
                public Date parse(String text, Locale locale) throws ParseException {
                    return FORMAT.get().parse(text);
                }
        
                @Override
                public String print(Date date, Locale locale) {
                    return FORMAT.get().format(date);
                }
            }
        }
        

        【讨论】:

        • 谢谢您,但能否请您添加导入语句?我不确定要导入哪些包。
        【解决方案4】:

        另一个简单的解决方案是使用默认的接口方法进行日期到字符串的转换,比如

        @RequestMapping(value = "/path", method = GET)
        List<Entity> byDate(@RequestParam("date") String date);
        
        default List<Entity> date(Date date) {
            return date(new SimpleDateFormat("dd.MM.yyyy").format(validityDate));
        }
        

        【讨论】:

        • 这是我能够使其与 feign-reactive 一起使用的唯一方法。
        【解决方案5】:

        您应该创建并注册一个 feign 格式化程序来自定义日期格式

        @Component
        public class DateFormatter implements Formatter<Date> {
        
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        
            @Override
            public Date parse(String text, Locale locale) throws ParseException {
                return formatter.parse(text);
            }
        
            @Override
            public String print(Date date, Locale locale) {
                return formatter.format(date);
            }
        }
        
        
        @Configuration
        public class FeignFormatterRegister implements FeignFormatterRegistrar {
        
            @Override
            public void registerFormatters(FormatterRegistry registry) {
                registry.addFormatter(new DateFormatter());
            }
        }
        

        【讨论】:

        • 解决方案效果很好。也许一些额外的信息会有用。通过实现事物 feign-client 解决方案,您不需要在 Feign 接口中使用 DateTimeFormat 注解。
        • 这个解决方案是错误的/危险的 imo。 SimpleDateFormat 是一个过时的类,您不应再使用它来支持joda 或新的java 格式化程序。 SimpleDateFormat 不是线程安全的。如果你想使用它,你应该将它包装在一个 ThreadLocal 中。 Spring 组件是单例的,因此每个 JVM 有 1 个实例,这意味着可能有不同的线程访问您的 SimpleDateFormat 并破坏它。
        • 此外,将此 DateFormatter 声明为组件,将此类注册为整个 spring 世界中的默认格式化程序,覆盖 spring 提供的所有默认日期格式化程序。鉴于在配置类中的使用,这无论如何都没有任何意义,因为您创建了日期格式化程序的新实例,而不是注入它并使用单例。因此,要么剥离组件注释,要么通过替换 SimpleDateFormat 或将其包装在 ThreadLocal 中来使类成为线程安全的。
        猜你喜欢
        • 2021-10-22
        • 2017-01-22
        • 2016-10-28
        • 2020-01-27
        • 2018-04-19
        • 2015-05-30
        • 2019-01-26
        • 2020-11-26
        • 2016-07-01
        相关资源
        最近更新 更多