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