java - Spring 反序列化 @RequestBody 中的 LocalDate 与 @RequestParam 中的 LocalDate 不同——为什么,它们可以相同吗?

标签 java json spring serialization java-8

问题:Spring 似乎对 LocalDate 使用不同的反序列化方法,具体取决于它是出现在 @RequestBody 还是请求 @ReqestParam -这是正确的吗?如果是这样,是否有办法将它们配置为在整个应用程序中相同?

背景:在我的@RestController 中,我有两种方法 - 一种是 GET,一种是 POST。 GET 需要一个类型为 LocalDate 的请求参数(“date”); POST 需要一个 JSON 对象,其中一个键(“日期”)的类型为 LocalDate。他们的签名类似于以下内容:

@RequestMapping(value = "/entity", method = RequestMethod.GET)
public EntityResponse get(
       Principal principal,
       @RequestParam(name = "date", required = false) LocalDate date) 

@RequestMapping(value = "/entity", method = RequestMethod.POST)
public EntityResponse post(
       Principal principal,
       @RequestBody EntityPost entityPost)

public class EntityPost {
       public LocalDate date;
}

我已经按如下方式配置了我的 ObjectMapper:

@Bean
public ObjectMapper objectMapper() {

   ObjectMapper objectMapper = new ObjectMapper();
   objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
   objectMapper.registerModule(new JavaTimeModule());
   objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

   return objectMapper;
}

这确保系统接受格式为 yyyy-MM-dd 的 LocalDate 并按预期反序列化它 - 至少当它是 @RequestBody 的一部分时。因此,如果以下是 POST 的请求正文

{
"date": 2017-01-01
}

系统按预期将请求正文反序列化为 EntityPost

但是,该配置不适用于 @RequestParam 的反序列化。结果,这失败了:

// fail!
/entity?date=2017-01-01

相反,系统似乎需要格式 MM/dd/yy。结果,这成功了:

// success!
/entity?date=01/01/17

我知道我可以使用@DateTimeFormat 批注逐个参数地更改它。我知道如果我按如下方式更改 GET 方法的签名,它将接受第一种格式:

@RequestMapping(value = "/entity", method = RequestMethod.GET)
public EntityResponse get(
       Principal principal,
       @RequestParam(name = "date", required = false) @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) LocalDate date) 

但是,如果我不必为 LocalDate 的每次使用都包含注释,我会更愿意。有没有办法全局设置它,以便系统以相同的方式反序列化 LocalDate 类型的每个 @RequestParam

供引用:

我正在使用 Spring 4.3.2.RELEASE

我正在使用 Jackson 2.6.5

最佳答案

根据@Andreas 的评论,Spring 框架使用Jackson 反序列化@RequestBody,但Spring 本身反序列化@RequestParam。这就是两者区别的根源。

answer显示如何使用 @ControllerAdvice@InitBinder 自定义 @RequestParam 的反序列化。我最终使用的代码如下:

import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

import java.beans.PropertyEditorSupport;
import java.text.Format;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.function.Function;

@ControllerAdvice
public class ControllerAdviceInitBinder {

    private static class Editor<T> extends PropertyEditorSupport {

        private final Function<String, T> parser;
        private final Format format;

        public Editor(Function<String, T> parser, Format format) {

            this.parser = parser;
            this.format = format;
        }

        public void setAsText(String text) {

            setValue(this.parser.apply(text));
        }

        public String getAsText() {

            return format.format((T) getValue());
        }
    }

    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {

        webDataBinder.registerCustomEditor(
                Instant.class,
                new Editor<>(
                        Instant::parse,
                        DateTimeFormatter.ISO_INSTANT.toFormat()));

        webDataBinder.registerCustomEditor(
                LocalDate.class,
                new Editor<>(
                        text -> LocalDate.parse(text, DateTimeFormatter.ISO_LOCAL_DATE),
                        DateTimeFormatter.ISO_LOCAL_DATE.toFormat()));

        webDataBinder.registerCustomEditor(
                LocalDateTime.class,
                new Editor<>(
                        text -> LocalDateTime.parse(text, DateTimeFormatter.ISO_LOCAL_DATE_TIME),
                        DateTimeFormatter.ISO_LOCAL_DATE_TIME.toFormat()));

        webDataBinder.registerCustomEditor(
                LocalTime.class,
                new Editor<>(
                        text -> LocalTime.parse(text, DateTimeFormatter.ISO_LOCAL_TIME),
                        DateTimeFormatter.ISO_LOCAL_TIME.toFormat()));

        webDataBinder.registerCustomEditor(
                OffsetDateTime.class,
                new Editor<>(
                        text -> OffsetDateTime.parse(text, DateTimeFormatter.ISO_OFFSET_DATE_TIME),
                        DateTimeFormatter.ISO_OFFSET_DATE_TIME.toFormat()));

        webDataBinder.registerCustomEditor(
                OffsetTime.class,
                new Editor<>(
                        text -> OffsetTime.parse(text, DateTimeFormatter.ISO_OFFSET_TIME),
                        DateTimeFormatter.ISO_OFFSET_TIME.toFormat()));

        webDataBinder.registerCustomEditor(
                ZonedDateTime.class,
                new Editor<>(
                        text -> ZonedDateTime.parse(text, DateTimeFormatter.ISO_ZONED_DATE_TIME),
                        DateTimeFormatter.ISO_ZONED_DATE_TIME.toFormat()));
    }
}

关于java - Spring 反序列化 @RequestBody 中的 LocalDate 与 @RequestParam 中的 LocalDate 不同——为什么,它们可以相同吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43684306/

相关文章:

java - Eclemma 2 个分支中的 1 个丢失

java - Springboot 应用程序 @EnableConfigServer 在 Eclipse 中无法解析

json - 如何使用 json 和 javascript/jquery 正确处理数据库时间值

java - STS + Spring Data JPA + Hibernate 存储库 XML 配置问题

java - 为什么我无法检索刚刚保留的实体?

java - Maven 不会运行 Android 仪器测试

java - 如何使用 Java 使用另一个 JSON 模式验证 JSON 模式结构?

javascript - Highcharts 饼图不接受值

java - spring MVC中的层结构

java - set.iterator()......我错在哪里?