
1. Overview
In this article, we will learn to perform the Spring Validation in Kotlin.
2. Kotlin Spring Validation
You can use validation annotations such as @NotBlank
, @NotNull
with Kotlin data classes.
If you add the validation annotations as it is to the constructor parameters of the data class, it won’t work. Let’s understand why.
Consider the following User
data class. We have placed the @NotBlank
annotation to the userId
constructor parameter.
data class User(@NotBlank @JsonProperty("userId") val userId: String?)
The Kotlin properties are compiled into a field, getter method, and if mutable (var
) a setter method.
If you declare the val
\ var
property in the primary constructor, it is compiled into a constructor parameter along with a backing field and getter/setter methods.
For example, the Koltin compiler compiles the userId
property in the primary constructor of the above User data class as constructor parameter and also as field:
public final data class User public constructor(@javax.validation.constraints.NotBlank @com.fasterxml.jackson.annotation.JsonProperty userId: kotlin.String?) { public final val userId: kotlin.String? /* compiled code */ public final operator fun component1(): kotlin.String? { /* compiled code */ } }
The annotation is applied only to the constructor parameter but not applied to the userId
field.
To make the Spring validation work, you should explicitly mention the location to apply the annotation like @field:annotation
or @get:annotation
in the constructor. There’re many locations where the annotation could be placed in this context: Kotlin property, backing field, getter method, constructor parameter, setter method, or setter parameter.
The field:
makes it explicit the annotation is to be applied to the backing field of the property.
For example, @field:NotBlank
is applied to the userId.
data class User(@field:NotBlank @JsonProperty("userId") val userId: String)
If you check the compiled User
class, the @NotBlank
annotation is applied to the field as expected and thus works.
package com.tedblob.springvalidation.controllers public final data class User public constructor(@com.fasterxml.jackson.annotation.JsonProperty userId: kotlin.String?) { @field:javax.validation.constraints.NotBlank public final val userId: kotlin.String? /* compiled code */ public final operator fun component1(): kotlin.String? { /* compiled code */ } }
Let’s validate this with a real-time example. The following UserController
accepts HTTP POST calls on URL /postUser
. It expects a body with non-null userId
.
package com.tedblob.springvalidation.controllers import com.fasterxml.jackson.annotation.JsonProperty 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.RequestMapping import org.springframework.web.bind.annotation.RestController import javax.validation.Valid import javax.validation.constraints.NotBlank @RestController @RequestMapping("/postUser") class UserController { @PostMapping fun postUser(@Valid @RequestBody request: User) : ResponseEntity<String> { return ResponseEntity.ok().build() } } data class User(@field:NotBlank @JsonProperty("userId") val userId: String)
For example, the below POST call will throw a Validation failure error.
http://localhost:8080/validation BODY: { "userId": null }
Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity<java.lang.String> com.tedblob.springvalidation.controllers.ValidationController.postUser(com.tedblob.springvalidation.controllers.User): [Field error in object 'user' on field 'userId': rejected value [null]; codes [NotBlank.user.userId,NotBlank.userId,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.userId,userId]; arguments []; default message [userId]]; default message [must not be blank]] ]
3. Conclusion
To sum up, we have learned to perform Spring validation in Kotlin.