OpenAPI date格式校验:requestBody中date字段未按yyyy-mm-dd规则校验问题
根本原因
两类参数的解析链路不同导致校验表现不一致:
- Query参数先做字符串格式校验再转换为
LocalDate,格式不匹配直接触发400 - RequestBody先由JSON序列化框架(通常是Jackson)完成字符串到
LocalDate的反序列化,默认宽松配置下会自动截断ISO时间戳的时间部分解析为合法LocalDate,不会抛出异常,且@Pattern注解仅对字符串类型生效,对LocalDate类型无效
以下是3种符合不删除format: date配置要求的可落地方案:
方案1:配置Jackson严格反序列化规则(推荐,改动最小)
直接修改Jackson全局反序列化配置,禁止LocalDate宽松解析,只要输入格式不是严格的yyyy-MM-dd就抛出解析异常,自动触发400返回。
如果是SpringBoot项目,直接在配置文件添加如下配置:
spring: jackson: deserialization: lenient: false date-format: yyyy-MM-dd
如果需要更精细的控制,可以自定义LocalDate反序列化器:
public class StrictLocalDateDeserializer extends StdDeserializer<LocalDate> { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); public StrictLocalDateDeserializer() { super(LocalDate.class); } @Override public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String value = p.getText().trim(); if (value.contains("T")) { // 匹配带时间戳的ISO格式 throw new JsonParseException(p, "Invalid date format, only yyyy-MM-dd is allowed"); } return LocalDate.parse(value, FORMATTER); } }
然后在OpenAPI的schema配置中给bodyDate字段添加扩展注解,指定使用该反序列化器:
bodyDate: type: string format: date x-field-extra-annotation: "@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = com.yourpackage.StrictLocalDateDeserializer.class)"
方案2:自定义校验注解实现格式校验
针对LocalDate类型无法使用@Pattern的问题,自定义校验注解完成格式校验:
- 定义校验注解
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = StrictDateValidator.class) public @interface StrictDate { String message() default "Invalid date format, only yyyy-MM-dd is allowed"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
- 实现校验器,从请求中拿到原始JSON值做格式校验
public class StrictDateValidator implements ConstraintValidator<StrictDate, LocalDate> { private static final Pattern DATE_PATTERN = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}$"); @Override public boolean isValid(LocalDate value, ConstraintValidatorContext context) { if (value == null) return true; // 非空校验由其他注解负责 // 从请求上下文获取原始请求body HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String body = request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); // 解析JSON获取对应的bodyDate原始值 try { JsonNode jsonNode = new ObjectMapper().readTree(body); String rawDate = jsonNode.get("bodyDate").asText(); return DATE_PATTERN.matcher(rawDate).matches(); } catch (Exception e) { return false; } } }
- 在OpenAPI配置中给
bodyDate添加该注解扩展即可
方案3:调整OpenAPI生成规则,字段用String类型接收
修改OpenAPI生成器配置,将format: date的请求体字段映射为String类型而非LocalDate,此时@Pattern注解即可正常生效,校验通过后再手动转换为LocalDate使用。
以openapi-generator-maven-plugin为例,添加如下配置:
<configuration> <typeMappings> <typeMapping>date=String</typeMapping> </typeMappings> <importMappings> <importMapping>java.time.LocalDate=</importMapping> </importMappings> </configuration>
同时在schema中保留pattern配置:
bodyDate: type: string format: date pattern: ^\d{4}-\d{2}-\d{2}$
内容的提问来源于stack exchange,提问作者Valéry
相关产品推荐
相关产品推荐

