Home » Kotlin Spring Validation

Kotlin Spring Validation

  • by
Kotlin Spring Annotation

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.

Leave a Reply

Your email address will not be published.