In my Spring Boot project, I've created a custom annotation with validator extending ConstraintValidator
to validate some fields in RequestBody. The annotation works fine for non-nested fields but validator is not called for nested ones.
My annotation looks like:
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = [CustomValidator::class])
@Suppress("unused")
@MustBeDocumented
annotation class CustomValidation(
val message: String = "validation failed",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = []
)
My validator class:
@Component
class CustomValidator : ConstraintValidator<CustomValidation, String> {
override fun isValid(field: String?, context: ConstraintValidatorContext?): Boolean {
if (field != "example") {
return false
}
return true
}
}
It works fine in cases like this:
data class MyRequest(
// validator works perfectly here
@JsonProperty("example") @CustomValidation val example: String? = null,
@JsonProperty("locale") val locale: String? = null
)
but when put on the nested object, a validator is not invoked:
data class MyRequest(
@JsonProperty("nested") val nested: NestedClass? = null,
@JsonProperty("locale") val locale: String? = null
)
data class NestedClass(
// validator not called in that case
@JsonProperty("example") @CustomValidation val example: String? = null
)
usage of MyRequest
class in my RestController
:
@PostMapping("/endpoint")
fun doSomething(
@Valid @RequestBody myRequest: MyRequest,
@RequestHeader(value = "token") token: String
): ResponseEntity<MyResponse> = ResponseEntity.ok(myService.getData(myRequest))
Any ideas about how to solve that problem?
I've already tried to put @Valid annotation on nested
field but it's still doesn't work
Usually for validation to work in a nested class, you need to annotate the field of the parent class with the same nested class type with @Valid
.
Here, for validation to work on the NestedClass
class, you would need to add @Valid
to the nested
field of MyRequest
class. And since it's part of the constructor, that should be done with Annotation Use-site Targets as below:
data class MyRequest(
@field:Valid @JsonProperty("nested") val nested: NestedClass? = null,
@JsonProperty("locale") val locale: String? = null
)
The reason why your @CustomValidation
annotation works without using the use-site target is since it has only a single target defined with @Target(AnnotationTarget.FIELD)
, whereas @Valid
annotation has multiple possible targets, ie @Target(value={METHOD,FIELD,CONSTRUCTOR,PARAMETER})
, and hence you need to have the use-site target to tell the compiler the right target.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With