SpringMVC请求参数接收总结(一) (6)

日期参数处理个人认为是请求参数处理中最复杂的,因为一般日期处理的逻辑不是通用的,过多的定制化处理导致很难有一个统一的标准处理逻辑去处理和转换日期类型的参数。不过,这里介绍几个通用的方法,以应对各种奇葩的日期格式。下面介绍的例子中全部使用Jdk8中引入的日期时间API,围绕java.util.Date为核心的日期时间API的使用方式类同。

一、统一以字符串形式接收

这种是最原始但是最奏效的方式,统一以字符串形式接收,然后自行处理类型转换,下面给个小例子:

@PostMapping(value = "/date1") public String date1(@RequestBody UserDto userDto) { UserEntity userEntity = new UserEntity(); userEntity.setUserId(userDto.getUserId()); userEntity.setBirthdayTime(LocalDateTime.parse(userDto.getBirthdayTime(), FORMATTER)); userEntity.setGraduationTime(LocalDateTime.parse(userDto.getGraduationTime(), FORMATTER)); log.info(userEntity.toString()); return "success"; } @Data public class UserDto { private String userId; private String birthdayTime; private String graduationTime; } @Data public class UserEntity { private String userId; private LocalDateTime birthdayTime; private LocalDateTime graduationTime; }

spmvc-p-12

使用字符串接收后再转换的缺点就是模板代码太多,编码风格不够简洁,重复性工作太多。

二、使用注解@DateTimeFormat或者@JsonFormat

@DateTimeFormat注解配合@RequestBody的参数使用的时候,会发现抛出InvalidFormatException异常,提示转换失败,这是因为在处理此注解的时候,只支持Form表单提交(Content-Type为x-www-form-urlencoded),例子如下:

spmvc-p-13

@Data public class UserDto2 { private String userId; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime birthdayTime; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime graduationTime; } @PostMapping(value = "/date2") public String date2(UserDto2 userDto2) { log.info(userDto2.toString()); return "success"; } //或者像下面这样 @PostMapping(value = "/date2") public String date2(@RequestParam("name"="userId")String userId, @RequestParam("name"="birthdayTime") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime birthdayTime, @RequestParam("name"="graduationTime") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime graduationTime) { return "success"; }

而@JsonFormat注解可使用在Form表单或者JSON请求参数的场景,因此更推荐使用@JsonFormat注解,不过注意需要指定时区(timezone属性,例如在中国是东八区GMT+8),否则有可能导致出现时差,举个例子:

@PostMapping(value = "/date2") public String date2(@RequestBody UserDto2 userDto2) { log.info(userDto2.toString()); return "success"; } @Data public class UserDto2 { private String userId; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime birthdayTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime graduationTime; } 三、Jackson序列化和反序列化定制

因为SpringMVC默认使用Jackson处理@RequestBody的参数转换,因此可以通过定制序列化器和反序列化器来实现日期类型的转换,这样我们就可以使用application/json的形式提交请求参数。这里的例子是转换请求Json参数中的字符串为LocalDateTime类型,属于Json反序列化,因此需要定制反序列化器:

@PostMapping(value = "/date3") public String date3(@RequestBody UserDto3 userDto3) { log.info(userDto3.toString()); return "success"; } @Data public class UserDto3 { private String userId; @JsonDeserialize(using = CustomLocalDateTimeDeserializer.class) private LocalDateTime birthdayTime; @JsonDeserialize(using = CustomLocalDateTimeDeserializer.class) private LocalDateTime graduationTime; } public class CustomLocalDateTimeDeserializer extends LocalDateTimeDeserializer { public CustomLocalDateTimeDeserializer() { super(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } } 四、最佳实践

前面三种方式都存在硬编码等问题,其实最佳实践是直接修改MappingJackson2HttpMessageConverter中的ObjectMapper对于日期类型处理默认的序列化器和反序列化器,这样就能全局生效,不需要再使用其他注解或者定制序列化方案(当然,有些时候需要特殊处理定制),或者说,在需要特殊处理的场景才使用其他注解或者定制序列化方案。使用钩子接口Jackson2ObjectMapperBuilderCustomizer可以实现对容器中的ObjectMapper单例中的属性定制:

@Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(){ return customizer->{ customizer.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); customizer.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); }; }

这样就能定制化MappingJackson2HttpMessageConverter中持有的ObjectMapper,上面的LocalDateTime序列化和反序列化器对全局生效。

请求URL匹配

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypwfy.html