Spring Boot中Kotlin类加@Valid后嵌套属性@NotNull校验不生效
核心原因
校验不触发是两个配置问题导致的:
- Kotlin data class 主构造函数上声明的注解,默认作用于构造器参数,而非类的字段。你在
b字段上添加的@Valid、@NotNull没有指定作用目标到字段,Spring Validation扫描字段级别的校验注解时识别不到这两个注解,自然不会触发对应校验。而a字段用了lateinit修饰,注解默认作用到字段上,所以a的校验可以正常生效。 - 去掉
b类型后的?会触发Jackson反序列化报错,是因为没有引入Jackson的Kotlin适配模块,Jackson原生不支持识别Kotlin data class的非空声明、默认值等语法特性,才会抛出异常。
解决方案
第一步:引入必要依赖
首先确保已经引入Spring校验和Jackson Kotlin模块的依赖,Spring Boot 2.3版本之后校验依赖不再默认随web starter引入,需要手动添加。
如果使用Gradle Kotlin DSL:
// 校验核心模块 implementation("org.springframework.boot:spring-boot-starter-validation") // Jackson Kotlin适配模块,解决反序列化Kotlin类报错问题 implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
如果使用Maven:
<!-- 校验核心模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- Jackson Kotlin适配模块 --> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-kotlin</artifactId> </dependency>
引入后Spring Boot会自动完成模块配置,不需要手动注册ObjectMapper的Kotlin模块。
第二步:修正模型类注解
给所有校验注解加上@field:前缀,明确指定注解作用于类字段,同时给必填字段设置默认值null,既避免反序列化报错,又能正常触发校验,不需要使用lateinit:
import java.util.Date import javax.validation.Valid import javax.validation.constraints.NotNull data class MyModel( @field:NotNull(message = "a is mandatory") var a: String? = null, @field:Valid @field:NotNull(message = "b is mandatory") var b: MyNestedModel? = null ) data class MyNestedModel( @field:NotNull(message = "MyNestedModel.a is mandatory") var a: Date? = null, @field:NotNull(message = "MyNestedModel.b is mandatory") var b: String? = null, @field:NotNull(message = "MyNestedModel.c is mandatory") var c: String? = null )
修改后逻辑完全符合预期:
- 当请求缺失
b参数时,Jackson会把b赋值为默认值null,不会抛出反序列化异常,之后@NotNull校验会正常触发,返回自定义的错误信息 - 当
b参数存在时,@Valid注解会生效,级联校验MyNestedModel内部的三个字段规则
可选优化:简化控制器代码
不需要手动注入Validator执行校验,Spring会自动完成校验逻辑,只要在@RequestBody参数前加上@Valid注解即可,控制器可以简化为:
import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController import javax.validation.Valid @RestController class MyController { @PostMapping("/aa") fun sortingRequest(@Valid @RequestBody req: MyModel): ResponseEntity<Any> { // 校验不通过时Spring会自动抛出MethodArgumentNotValidException,可以通过全局异常捕获统一返回错误格式 return ResponseEntity<Any>(null, HttpStatus.CREATED) } }
如果希望保留手动调用validator.validate()的写法也可以,修正注解后手动调用的逻辑也能正常识别b字段的@NotNull和@Valid规则,级联校验嵌套对象。
内容的提问来源于stack exchange,提问作者Code_Worm
相关产品推荐
相关产品推荐

