Spring Boot 1.5升级至2.7后Spring Data Rest POST请求返回400状态码问题排查
这个问题的根源是Spring Boot 2.x版本升级后,Jackson Kotlin模块对非空构造参数的校验逻辑变得更严格。你的父类Resource中的tags字段虽然定义了默认值Collections.emptySet(),但在反序列化HAL格式的请求体时,Jackson没有正确识别这个默认值——因为请求中没有传递tags字段,直接触发了MissingKotlinParameterException。
下面是几个可行的解决方案,按推荐优先级排序:
方案1:给父类构造参数添加@JsonProperty(required = false)
你之前尝试在子类Feed上添加注解没用,因为tags是父类Resource的属性,需要直接在父类的构造参数上标记:
abstract class Resource( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) override val id: Long, @JsonProperty(required = false) // 关键:添加在这里 @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "resource_tags", joinColumns = [JoinColumn(name = "resource_id")], inverseJoinColumns = [JoinColumn(name = "tag_id")] ) val tags: Set<Tag> = Collections.emptySet(), @JsonIgnore @OneToMany(mappedBy = "resource", cascade = [CascadeType.REMOVE]) val menuItems: List<Menu> = ArrayList(0), // 其他字段 )
这个方案侵入性最小,不需要修改全局配置或实体的可变性,直接告诉Jackson这个参数不是必须的,会自动使用默认值。
方案2:全局配置Jackson Kotlin模块,启用Null转空集合特性
如果你的项目中有多个类似的集合字段问题,可以通过全局配置一次性解决:
@Bean(name = arrayOf("OBJECT_MAPPER_BEAN")) fun jsonObjectMapper(): ObjectMapper { // 自定义Kotlin模块,启用NullToEmptyCollection特性 val kotlinModule = KotlinModule.Builder() .configure(KotlinFeature.NullToEmptyCollection, true) .configure(KotlinFeature.NullToEmptyMap, true) // 可选:同时处理Map类型 .build() return Jackson2ObjectMapperBuilder.json() .serializationInclusion(JsonInclude.Include.ALWAYS) .featuresToDisable( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS ) .modules(JavaTimeModule(), kotlinModule) // 替换默认的Kotlin模块 .build<ObjectMapper>() }
这个配置会让Jackson自动将缺失的集合类型参数转换为空集合,避免非空校验错误,适合全局统一处理的场景。
方案3:修改实体字段为可变属性(不推荐,除非业务允许)
如果你的业务逻辑允许修改tags集合,可以将val改为var,这样Jackson在反序列化时会先创建实例,再设置默认值:
@ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "resource_tags", joinColumns = [JoinColumn(name = "resource_id")], inverseJoinColumns = [JoinColumn(name = "tag_id")] ) var tags: Set<Tag> = Collections.emptySet(),
这个方案会破坏实体的不可变性设计,只有在业务上必须修改tags时才考虑使用。
为什么升级前没问题?
Spring Boot 1.5使用的Jackson版本(约2.8.x)对应的Kotlin模块对非空构造参数的处理比较宽松,即使请求中没有传递字段,也会自动使用默认值;而Spring Boot 2.7使用的Jackson版本(约2.13.x)的Kotlin模块默认开启了严格的非空校验,必须显式处理缺失的参数。
内容的提问来源于stack exchange,提问作者Shadow4571

