Kotlin数据类结合SpringBoot Validation校验Null失效,如何解决?
问题场景
定义了如下Kotlin数据类,使用@NotNull做非空校验:
data class GetSourceListRequest( @NotNull(message = "category not allow null") val category: List<Int> )
通过@ControllerAdvice处理异常:
@ExceptionHandler(value = [HttpMessageNotReadableException::class]) fun <T> httpMessageNotReadableExceptionHandler(e: HttpMessageNotReadableException): String { return e.message }
当请求JSON中缺失category字段时,得到的错误信息是:
JSON parse error: Instantiation of [simple type, class com.sage.feature.source.database.request.GetSourceListRequest] value failed for JSON property category due to missing (therefore NULL) value for creator parameter category which is a non-nullable type
自定义的@NotNull注解未生效,因为Jackson在JSON反序列化环节先抛出了异常,跳过了Spring Validation校验流程。
解决方案
1. 配置Jackson允许缺失构造参数
Kotlin数据类的非空构造参数,Jackson默认会在缺失对应JSON字段时直接抛异常。需要关闭这个特性,让Jackson将缺失字段设为null,再交给Validation校验。
方式一:通过配置文件
在application.yml中添加:
spring: jackson: deserialization: fail-on-missing-creator-properties: false
方式二:通过配置类
@Configuration class JacksonConfig { @Bean fun jackson2ObjectMapperBuilderCustomizer(): Jackson2ObjectMapperBuilderCustomizer { return Jackson2ObjectMapperBuilderCustomizer { builder -> builder.failOnMissingCreatorProperties(false) } } }
2. 开启Controller层的Validation校验
在Controller的请求参数上添加@Valid或@Validated注解,触发校验流程:
@PostMapping("/source/list") fun getSourceList(@RequestBody @Valid request: GetSourceListRequest): ResponseEntity<Any> { // 业务逻辑处理 return ResponseEntity.ok().build() }
3. 扩展异常处理器,捕获Validation异常
添加MethodArgumentNotValidException的处理器,返回自定义的校验错误信息:
@ControllerAdvice class GlobalExceptionHandler { @ExceptionHandler(HttpMessageNotReadableException::class) fun handleHttpMessageNotReadable(e: HttpMessageNotReadableException): ResponseEntity<Map<String, String>> { return ResponseEntity.badRequest() .body(mapOf("error" to "JSON格式解析错误")) } @ExceptionHandler(MethodArgumentNotValidException::class) fun handleValidationError(e: MethodArgumentNotValidException): ResponseEntity<Map<String, String>> { val errorMap = e.bindingResult.allErrors.associate { val fieldError = it as FieldError fieldError.field to fieldError.defaultMessage.orEmpty() } return ResponseEntity.badRequest().body(errorMap) } }
此时当请求缺失category字段时,会触发@NotNull校验,返回{"category": "category not allow null"}的错误响应。
内容的提问来源于stack exchange,提问作者SageJustus

