By default @Valid annotation on constructor arguments tells Spring to validate object fields. But Kotlin places constraint annotations on the constructor parameters, so validation doesn’t work. To make it work I have to define use-site targets for annotations:
data class Credentials(@field:Email @field:NotBlank val email: String, @field:NotBlank val password: String)
which is annoying and adds visual garbage. Is it possible to configure Spring to validate constructor parameters?
The problem is that Kotlin code has to compile to JVM class bytecode. The inline constructor is just a syntactic sugar which results in a constructor, getters and setters being generated in the .class
file.
So the above is just the idiomatic Kotlin way of going around this.
As per Kotlin docs:
If you don’t specify a use-site target, the target is chosen according to the @Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
- param;
- property;
- field
One possible way of cleaning this up is to declare your own annotations which have @Target(FIELD)
only. This way the compiler will infer the target accordingly:
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
@Constraint(validatedBy = arrayOf())
@NotBlank
@Email
annotation class ValidEmail constructor(
val message: String = "ignored message",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = []
)
then
data class Credentials(@ValidEmail val email: String, ...
One advantage of doing this is that you can combine multiple standard JSR-303 constraints into a single domain-specific annotation (as in the email example above, although @Email
already implies @NotBlank
so not the best illustration)
2 Likes
I do this, but validation not work again.
1 Like