인터페이스에서 시간 유형 매개변수를 우아하게 수신하는 방법

첫 번째 출시: 공개 계정 " Zhao Xiake "

머리말

위에서 우리는 프론트엔드와 백엔드 HTTP 인터페이스에서 매개변수를 전달하는 일반적인 방법을 요약했습니다. 이 기사에서는 주로 매개변수의 시간 필드를 처리하는 방법을 요약합니다. 시간 형식이 많기 때문에 더 일반적으로 사용되는 형식입니다. 타임스탬프 형식, UTC 시간 형식, 표준 시간 형식 등이 있으며 시간 매개변수는 URL, 본문 또는 헤더에 나타날 수 있으므로 이 문서에서는 시간 형식 필드 처리를 위한 일련의 우아한 솔루션을 제공합니다.

시간 형식이 작업 처리를 수행하지 않으면 어떻게 되나요?

@PathVariableDate 유형 및 LocalDateTime 유형의 시간 매개변수를 수신 하려는 간단한 인터페이스를 작성하고 다음을 통해 JSON에서 시간 매개변수를 수신 @RequestParam하려고 합니다 .@RequestBody

    @GetMapping("/time/{today}")
    public UserDTO time(@PathVariable Date today, @RequestParam LocalDateTime time,@RequestBody UserDTO userDTO) {
    
    
        return userDTO;
    }
    
    @Data
    public class UserDTO {
    
    
        private Long id;
        private String userName;
        private Date  now;
        private Date  day;
        private LocalDateTime time;
        private LocalDateTime timeStack;
    }

HTTP 테스트 요청 메시지:

GET http://localhost:80/time/2023-09-10?time=2023-09-15 11:11:11
Accept: application/json
Content-Type: application/json

{
    
    
    "id":1,
    "now":"2023-09-15 13:50:10",
    "day":"2023-09-15",
    "time": "2023-09-15 13:50:10",
    "timeStack": 1694757010407
}

결과:

작업 처리를 하지 않으면 SpringBoot는 자동으로 인터페이스의 시간 매개 변수를 원하는 시간 형식으로 변환하는 데 도움을 줄 수 없습니다. 기본값은 String을 사용하여 수신하는 것입니다. LocalDateTime 또는 Date를 사용하여 직접 수신하는 경우 유형 변환 오류가 보고됩니다. 이 역시 시간 형식이 너무 많기 때문에 상대적으로 이해하기 쉽습니다. 구체적인 시간 형식을 모르면 프레임워크에서는 시간을 구문 분석할 수 없고 String만 사용하여 수신할 수 있습니다. 마지막으로 String을 시간 형식으로 변환하면, 오류가 확실히 보고됩니다. 물론 String을 사용하여 이를 받아 해당 시간 형식으로 수동으로 변환할 수도 있지만 이 방법은 너무 원시적이므로 다음으로 시간 필드가 다양한 수준에서 어떻게 처리되는지 살펴보겠습니다.


Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.time.LocalDateTime'; 
Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date';

청동 용액

SpringMVC는 매개변수를 수신할 때 자동으로 JAVA 객체에 매개변수를 주입한다는 것을 알고 있습니다 WebDataBinder. SpringMVC는 매개변수를 수신하기 전에 매개변수 구문 분석을 초기화하는 기능을 제공합니다 @InitBinder. 그런 다음 컨트롤러에 이를 추가한 @InitBinder다음 WebDataBinder객체로 이동하여 두 개의 CustomEditor인 LocalDateTime을 사용자 정의 할 수 있습니다. 및 Date를 @PathVariable사용 @RequestParam하면 자동으로 문자열을 시간 형식으로 변환할 수 있습니다 . 하지만 @RequestBodyJSON 데이터 파싱에는 기본적으로 Jackson이 사용되기 때문에 여전히 객체의 시간 형식을 처리할 수 없으며, 시간 필드에 주석을 추가하여 시간 @JsonFormat형식을 지정하면 시간 @RequestBody형식을 자동으로 파싱할 수 있습니다.

    @InitBinder
    public void initBinder(WebDataBinder binder) {
    
    
        binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
    
    
            @Override
            public void setAsText(String text) {
    
    
                setValue(DateUtil.parseLocalDateTime(text));
            }
        });

        binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
    
    
            @Override
            public void setAsText(String text) {
    
    
                setValue(DateUtil.parse(text,NORM_DATETIME_PATTERN,NORM_DATE_PATTERN));
            }
        });
    }
    
    
    @Data
    public class UserDTO {
    
    
        private Long id;
        private String userName;
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date  now;
        @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
        private Date  day;
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private LocalDateTime time;
        //private LocalDateTime timeStack;
    }

청동 분석 솔루션의 문제점:

  1. @InitBinder범위는 현재 Controller 뿐입니다. Controller 100개 사용하면 100개 더 써야 하나요?@InitBinder
  2. @JsonFormat또한 각 필드에 주석을 추가해야 하는데 이는 하나의 시간 형식만 지원할 수 있으며, 타임스탬프 형식도 지원하려는 경우에는 불가능합니다.

은 용액

브론즈 해결 솔루션의 문제 1 에 대한 솔루션은 다음을 사용하는 @ControllerAdvice것 입니다.@InitBinder

@ControllerAdvice
public class GlobalControllerAdvice {
    
    

    @InitBinder
    public void initBinder(WebDataBinder binder) {
    
    
        binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
    
    
            @Override
            public void setAsText(String text) {
    
    
                setValue(DateUtil.parseLocalDateTime(text));
            }
        });
        binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
    
    
            @Override
            public void setAsText(String text) {
    
    
                setValue(DateUtil.parse(text,NORM_DATETIME_PATTERN,NORM_DATE_PATTERN));
            }
        });
    }
}

브론즈 솔루션의 문제 2와 관련하여 우리의 분석은 SpringMvc가 Jackson을 사용하여 JSON을 구문 분석하므로 SpringMVC가 사용자 정의 솔루션 을 사용 하여 MapperJSON을 구문 분석 하도록 할 수 있다는 것 입니다 . 각 필드를 추가할 필요가 없습니다 . Jaskson이 JSON 데이터를 구문 분석할 때 LocalDateTime이라는 매개 변수 수신 유형을 발견하면 사용자 정의 프로세서를 직접 사용하므로 필드 변환 오류를 보고하지 않습니다. 이제 작성하는 것이 훨씬 더 우아해졌습니다 . 하나씩?@ConfigurationObjectMapper
LocalDateTimeSerializerLocalDateTimeDeserializer@JsonFormat@JsonFormat

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
    
    
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        module.addSerializer(Date.class, new DateTimeSerializer());
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        module.addDeserializer(Date.class, new DateTimeDeserializer());
        mapper.registerModule(module);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return JsonUtils.getMapper();
    }
}

public class DateTimeDeserializer extends StdDeserializer<Date> {
    
    

    public DateTimeDeserializer() {
    
    
        this(null);
    }

    public DateTimeDeserializer(Class<?> vc) {
    
    
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser jp, DeserializationContext ctx)
            throws IOException {
    
    
        String value = jp.getValueAsString();
            return DateUtil.parse(value,NORM_DATETIME_PATTERN,NORM_DATE_PATTERN);
    }
}

public class DateTimeSerializer extends StdSerializer<Date> {
    
    

    public DateTimeSerializer() {
    
    
        this(null);
    }

    public DateTimeSerializer(Class<Date> t) {
    
    
        super(t);
    }

    @Override
    public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
    
    
        jgen.writeString(DateUtil.format(value, DatePattern.NORM_DATETIME_PATTERN));
    }
}

public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
    
    

    public LocalDateTimeDeserializer() {
    
    
        this(null);
    }

    public LocalDateTimeDeserializer(Class<?> vc) {
    
    
        super(vc);
    }

    @Override
    public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctx)
            throws IOException {
    
    
        String value = jp.getValueAsString();
        if (StrUtil.isNumeric(value)) {
    
    
            Date date = new Date(jp.getLongValue());
            return LocalDateTime.ofInstant(date.toInstant(),  ZoneId.of("Asia/Shanghai"));
        } else {
    
    
            return DateUtil.parseLocalDateTime(value);
        }
    }
}

public class LocalDateTimeSerializer extends StdSerializer<LocalDateTime> {
    
    

    public LocalDateTimeSerializer() {
    
    
        this(null);
    }

    public LocalDateTimeSerializer(Class<LocalDateTime> t) {
    
    
        super(t);
    }

    @Override
    public void serialize(LocalDateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
    
    
        jgen.writeString(LocalDateTimeUtil.formatNormal(value));
    }
}


문제가 있습니다:

  1. @ControllerAdvice측면을 기준으로 가로채려면 각 인터페이스를 가로채야 하는데 성능이나 우아함이 별로 좋지 않은데 잭슨처럼 우아하게 다룰 수 있을까요?

킹 솔루션

시간 유형을 변환하기 위해 변환기를 추가하고 사용자 정의 Configuration하므로 Converter<String, LocalDateTime> stringLocalDateTimeConverter()JSON 데이터 매개변수, URL 매개변수 또는 헤더 매개변수를 전달하는 경우 수신하는 시간 유형이 Date인지 LocalDateTime인지는 중요하지 않습니다. 당신의 시간 형식 표준 시간 형식이든 타임 스탬프이든 모두 자동으로 시간 수신 문제를 자동으로 해결합니다. 이것이 더 우아하지 않습니까?Converter<String, Date> stringDateTimeConverter()

  
@Configuration
public class WebConfig  {
    
    
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
    
    
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        module.addSerializer(Date.class, new DateTimeSerializer());
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        module.addDeserializer(Date.class, new DateTimeDeserializer());
        mapper.registerModule(module);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return JsonUtils.getMapper();
    }

    @Bean
    public Converter<String, LocalDateTime> stringLocalDateTimeConverter() {
    
    
        return new Converter<String, LocalDateTime>() {
    
    
            @Override
            public LocalDateTime convert(String source) {
    
    
                if (StrUtil.isNumeric(source)) {
    
    
                    return LocalDateTimeUtil.of(Long.parseLong(source));
                } else {
    
    
                    return DateUtil.parseLocalDateTime(source);
                }
            }
        };

    }

    @Bean
    public Converter<String, Date> stringDateTimeConverter() {
    
    
        return new Converter<String, Date>() {
    
    
            @Override
            public Date convert(String source) {
    
    
                if (StrUtil.isNumeric(source)) {
    
    
                    return new Date(Long.parseLong(source));
                } else {
    
    
                    return DateUtil.parse(source);
                }
            }
        };
    }

}

요약하다

이 기사에서는 SpringBoot 프로젝트 개발 중에 HTTP 프로토콜에서 시간 유형 매개변수를 우아하게 수신하는 방법을 소개합니다. 시간 매개변수는 URL Path, queryString, FormData, BodyJSON, HTTP 헤더에 나타날 수 있으며, 시간 형식은 헤더 형식, 타임스탬프, 수신 시간 매개변수는 Date 또는 LocalDateTime이 될 수 있으며 인터페이스의 시간 유형 필드 문제를 해결합니다. 글로벌하게 아주 우아하게. .

추천

출처blog.csdn.net/whzhaochao/article/details/132939209