You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot 1.5升级至2.7后Spring Data Rest POST请求返回400状态码问题排查

解决Spring Boot 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 17:24:08