.net webservice怎么接收post过来的json_SpringBoot中时间戳和LocalDate相关的接收和转换

dda78517d1c4aa251260d43e8e44fa40.png

背景

  1. 前端和后端在时间格式的传递上都走的是时间戳(方便前端自由定制)
  2. 时间格式大多用的是LocalDate,并不是传统的Date

简单来说就是时间戳在各类形式下转日期类。

当有如下类似代码的时候,后端该做哪些配置来满足上述需求了?

    @PostMapping("/date-test/post/{pathDate}")    public LocalDateTime getDatePost(@RequestParam LocalDate date,                                     @RequestParam LocalDateTime dateTime,                                     @RequestParam Date originalDate,                                     @PathVariable("pathDate") LocalDateTime datePath,                                     LocalDate localDate,                                     @RequestBody Person person,                                     Person person2) {        System.out.println(date);        System.out.println(dateTime);        System.out.println(originalDate);        System.out.println(localDate);        System.out.println(datePath);        System.out.println(person);        System.out.println(person2);        return LocalDateTime.now();    }}

上述案例使用了post来接收参数,如果你是get请求,除了@RequestBody不能使用外,其他不受影响

请求的URL如下所示:

localhost:8099/date-test/post/1568796069368?date=1568796069368&localDate=1568796069368&dateTime=1568796069368&originalDate=1568796069368&name=codingOX&birth=1568796069368

其中json部分的配置如下

072f831b7a54d91bd1d26be895dd40ca.png

正文

SpringMVC处理参数,其核心接口是org.springframework.web.method.support.HandlerMethodArgumentResolver

相关的实现挺多的,如下所示,重点部分我都通过红色框框进行了标记。

c71cfd7b3b9cb4bad6a3dc4ba8684620.png

我们通过接口来分析下SpringMVC是如何处理参数的

public interface HandlerMethodArgumentResolver {/** * 判定当前处理器适合处理那种类型的数据 */boolean supportsParameter(MethodParameter parameter);/** * 对符合预期的数据,进行具体的逻辑处理 */@NullableObject resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}

更详细的代码你可以查看上图给出的实现,整体而言就是根据根据supportsParameter的结果Boolean来判定当前类是否可以解析这个参数,如果可以继续调用resolveArgument对参数进行解析。

后面我们需要利用该特点来自定义处理类,下面将通过如下几种情况进行处理。

  • 通过@RequestParam和@PathVariable修饰LocalDate
  • 通过@RequestBody修饰LocalDate
  • 不对LocaDate添加任何修饰
  • 不对包含LocalDate的对象添加任何修饰

@RequestParam和@PathVariable

这种情况对应的是上面例子形参中的:

    public LocalDateTime getDatePost(@RequestParam LocalDate date,                                     ....                                     @PathVariable LocalDateTime datePath,                                     ....) {...}

Spring的源码部分,通过文字分析和解读起来太累,这里直接给出结论,下同,有兴趣的可以结合结论,反过来阅读。。。

这2个类对应的处理器分别是:

  • org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
  • org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver

如果有兴趣,可以跟下源码,你可以发现,处理这2类注解的解析主要用到的是:WebDataBinder,如果要扩展这类参数的处理方式,可以使用org.springframework.core.convert.converter.Converter或者org.springframework.format.Formatter

这2者区别在于:

  • Converter可以定义输入和输出
  • Formatter默认输入是字符串,输出为自定义

你可以根据自身需求进行定制,这里我通过Convert转换LocalDate为例,进行转换:

/** * 接收 形参加有注解  @RequestParam和@PathVariable时候 字符串转Date相关类 * * @author Liu Chunfu * @date 2019-09-18 3:57 下午 **/public class CustomDateConverter {      public static class LocalDateConvert implements Converter {        @Override        public LocalDate convert(String timestamp) {            return LocalDateUtil.timestampToLocalDateTime(timestamp).toLocalDate();        }    }     ...}

定义后,还需要使用,这个使用也很简单,将其注入Spring的容器就可以了。

  • 如果是Spring MVC,通过XML定义为类
  • 如果是Spring Boot,在@Configuration下通过@Bean声明就可以了。

还是举个例子吧:

@Configurationpublic class CustomDateConfig{    @Bean    public Converter localDateConverter() {        //此处不能替换为lambda表达式        return new CustomDateConverter.LocalDateConvert();    }}

通过@RequestBody修饰

通过@RequestBody很明显的就是要通过JSON序列化进行处理,Spring默认的是Jackson进行处理,所以我们需要对默认的JSON处理器进行日期类型的添加(假如默认不带对应序列化器的情况下)

此时需要用的是JsonDeserializer这个类,对应的实现如下所示:

public class CustomDateDeserializer {    /**     * 反序列化LocalDateTime     */    public static class LocalDateDeserializer extends JsonDeserializer {        @Override        public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {            Long timestamp = Long.valueOf(p.getText());            Instant instant = Instant.ofEpochMilli(timestamp);            LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));            LocalDate localDate = localDateTime.toLocalDate();            return localDate;        }    }    ....}

有了这个了类,还需要添加到Spring中,Spring利用的是ObjectMapper,我们自定义一个ObjectMapper替换原本的就可以了。

@Configurationpublic class CustomDateConfig  {  // 省略部分代码    /**     * Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json     */    @Bean    public ObjectMapper objectMapper() {        ObjectMapper objectMapper = new ObjectMapper();        //不显示为null的字段        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);        // 忽略不能转移的字符        objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);        // 过滤对象的null属性.        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);        //忽略transient        objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);        //LocalDateTime系列序列化和反序列化模块,继承自jsr310,我们在这里修改了日期格式        JavaTimeModule javaTimeModule = new JavaTimeModule();        // LocalDateTime        javaTimeModule.addSerializer(LocalDateTime.class, new CustomDateSerializer.LocalDateTimeSerializer());        javaTimeModule.addDeserializer(LocalDateTime.class,new CustomDateDeserializer.LocalDateTimeDeserializer());        // LocalDate        javaTimeModule.addSerializer(LocalDate.class, new CustomDateSerializer.LocalDateSerializer());        javaTimeModule.addDeserializer(LocalDate.class, new CustomDateDeserializer.LocalDateDeserializer());        //Date序列化和反序列化        javaTimeModule.addSerializer(Date.class,new CustomDateSerializer.DateSerializer());        javaTimeModule.addDeserializer(Date.class,new CustomDateDeserializer.DateDeserializer());        objectMapper.registerModule(javaTimeModule);        return objectMapper;    }  // 省略部分代码}

外部参数什么注解也不加

这种情况对应的是形参中的:

public LocalDateTime getDatePost(...,LocalDate localDate,....) { .... }

这种情况下,通过Spring默认的ModelAttributeMethodProcessor进行处理,其处理逻辑为:通过反射创建一个对象,然会在反射对每个参数通过Convert进行转换赋值。

默认情况下,此种情况是会报错的,因为LocalDate没有构造函数。所以我们需要在默认调用之前,自定义处理这此类形参,此时利用的就是HandlerMethodArgumentResolver这个接口。

HandlerMethodArgumentResolver会在ModelAttributeMethodProcessor之前调用,该接口的声明非常简单。

todo

我们可以对此类参数进行自定义。

public class CustomDateArgumentResolverHandler {    public static class LocalDateArgumentResolverHandler implements HandlerMethodArgumentResolver {        @Override        public boolean supportsParameter(MethodParameter parameter) {            return parameter.getParameterType().equals(LocalDate.class);        }        @Override        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,                                      NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {            String timestamp = webRequest.getParameter(parameter.getParameterName());            LocalDateTime localDateTime = LocalDateUtil.timestampToLocalDateTime(timestamp);            return localDateTime.toLocalDate();        }    }    ......}

在使用该类的时候,需要在配置类@Configuration中通过WebMvcConfigurer这个接口下的方法addArgumentResolvers来添加进去:

public void addArgumentResolvers(List resolvers) 

大体的代码如下:

@Configurationpublic class CustomDateConfig implements WebMvcConfigurer {    @Override    public void addArgumentResolvers(List resolvers) {        resolvers.add(new CustomDateArgumentResolverHandler.LocalDateArgumentResolverHandler());        // .....    }}

内部参数什么注解也不加

对应的情况是:

public LocalDateTime getDatePost(Person person2) 

此时的处理情况符合上述提到的ModelAttributeMethodProcessor和Convert处理。

所以如果我们按照上面提到的方法进行了处理,那么不用担心时间类型无法解析的问题。

结语

关于时间LocalDate这个类型的处理都完成了,上面的代码还是比较凌乱的,改天我单独做一个Demo,放在GitHub上,不过我还是希望你手动自己试试!

如果本文对你有帮助,欢迎评论、点赞、转发、收藏、关注!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值