SpringBoot中的@RequestParam在传递时间类型时发生异常,传入的时间无法被解析。我们一起研究一下如何在请求和应用程序级别上在Spring REST请求中接受Date,LocalDate和LocalDateTime参数。
一、异常举例
Failed to convert value of type 'java.lang.String' to required type 'java.time.LocalDate';
nested exception is org.springframework.core.convert.ConversionFailedException.
二、举例复现
@RestController
public class DateTimeController {
@PostMapping("/date")
public void date(@RequestParam("date") Date date) {
// ...
}
@PostMapping("/localdate")
public void localDate(@RequestParam("localDate") LocalDate localDate) {
// ...
}
@PostMapping("/localdatetime")
public void dateTime(@RequestParam("localDateTime") LocalDateTime localDateTime) {
// ...
}
}
在调用上述接口的时候,就会复现异常。
即使我们在application.properties中配置了接受的时间格式也不会生效,因为这时不需要进行序列化处理,根本没有经过Jackson的处理。
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
三、解决方案
1. 使用注解 @DateTimeFormat
使用
@DateTimeFormat直接指定接受的时间格式,即灵活又方便。
@PostMapping("/date")
public void date(@RequestParam("date") @DateTimeFormat(pattern = "yyyy-MM-dd") Date date) {
// ...
}
2. 使用@InitBinder
仅对当前的
Controller起作用,不推荐。
@InitBinder
protected void initBinder(WebDataBinder binder)
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
3. 全局配置
可以参考
Spring官方给出的方案,https://www.baeldung.com/spring-date-parameters 不推荐。
@Configuration
class DateTimeConfig {
@Bean
public FormattingConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setDateFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
registrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
registrar.registerFormatters(conversionService);
// 注册其他Formatters
return conversionService;
}
}
在DateTimeFormatterRegistrar对象中手动注册LocalDate和LocalDateTime的时间格式为yyyy-MM-dd 和 "yyyy-MM-dd HH:mm:ss"。
遗憾的是,并没有对Date进行处理,看源码,确实只是针对LocalDate进行的格式设置的。
/**
* Set the formatter that will be used for objects representing date values.
* <p>This formatter will be used for the {@link LocalDate} type.
* When specified, the {@link #setDateStyle dateStyle} and
* {@link #setUseIsoFormat useIsoFormat} properties will be ignored.
* @param formatter the formatter to use
* @see #setTimeFormatter
* @see #setDateTimeFormatter
*/
public void setDateFormatter(DateTimeFormatter formatter) {
this.formatters.put(Type.DATE, formatter);
}
四、扩展悦读
FormatterRegistrar 提供三种实现
DateFormatterRegistrar是Spring 3.2 版本之后针对Date,Calendar,long这三种类型进行解析。如上第三种全局解决方案中,如果要对Date进行处理,那么就注册这个。DateTimeFormatterRegistrar是Spring 4.0 版本之后针对JDK8的时间类型LocalDate、LocalTime、LocalDateTime这三种格式进行解析,也就是第三种全局解决方案中注册的。JodaTimeFormatterRegistrar是Spring 3.1 版本之后针对第三方Joda的实现,如果想要使用,那么需要引入Joda-Time的依赖。解析的类型是Joda中的时间类型,随着JDK8的广泛使用,已经很少使用Joda了。
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
本文使用 mdnice 排版