Spring deserializes a LocalDate in a @RequestBody differently from one in a @RequestParam - why, and can they be the same?

前端 未结 2 1087
遥遥无期
遥遥无期 2021-02-15 17:27

QUESTION: Spring appears to use different deserialization methods for LocalDate depending on whether it appears in a @RequestBody or a request @R

相关标签:
2条回答
  • 2021-02-15 17:53

    Create a Formatter for LocalDate:

    public class LocalDateFormatter implements Formatter<LocalDate> {
    
        @Override
        public LocalDate parse(String text, Locale locale) throws ParseException {
            return LocalDate.parse(text, DateTimeFormatter.ISO_DATE);
        }
    
        @Override
        public String print(LocalDate object, Locale locale) {
            return DateTimeFormatter.ISO_DATE.format(object);
        }
    }
    

    Spring 5+: Register the formatter: Implement WebMvcConfigurer in your @Configuration and override addFormatters:

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new LocalDateFormatter());
    }
    

    Spring Boot: Define a @Primary @Bean to override the default formatter:

    @Bean
    @Primary
    public Formatter<LocalDate> localDateFormatter() {
        return new LocalDateFormatter();
    }
    
    0 讨论(0)
  • 2021-02-15 17:59

    Per @Andreas in comments, the Spring Framework uses Jackson to deserialize @RequestBody but Spring itself deserializes @RequestParam. This is the source of the difference between the two.

    This answer shows how to use @ControllerAdvice and @InitBinder to customize the deserialization of @RequestParam. The code I ultimately used follows:

    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()));
        }
    }
    
    0 讨论(0)
提交回复
热议问题