简体   繁体   中英

Kotlin Spring Boot bean validation not working

I have quite a few projects that is slowly being migrated from Java to Kotlin, but I'm facing a problem when changing from Java POJO to Kotlin data classes. Bean validation stops working in REST controllers. I have created a very simple project directly from https://start.spring.io to demonstrate the failure.

@SpringBootApplication
class RestValidationApplication

fun main(args: Array<String>) {
    runApplication<RestValidationApplication>(*args)
}

@RestController
class Controller {

    @PostMapping
    fun test(@Valid @RequestBody request: Test) {
        println(request)
    }
}

data class Test(@field:NotNull val id: String)

and gradle:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.6.1"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
    kotlin("jvm") version "1.6.0"
    kotlin("plugin.spring") version "1.6.0"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_17

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-validation")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
} 

Sending a request does not trigger the bean validation, but it throws a HttpMessageNotReadableException because id is NULL.

curl -X POST http://localhost:8080 -H "Content-Type: application/json" -d "{}"

I have tried both @Valid and @Validated and using @get:NotNull on the attributes, but nothing works. I see many others have the same problem and using a Java class from a Kotlin REST controller works. Also changing to a non data class makes validation works. Anyone know if it's possible to get bean validation working with Kotlin data classes?

Do not use data class for validation

when you use data class you need to add @field:{} before annotation or you can use class without data class.

data class:

class AuthorInput(
    @field:NotBlank @field:Size(max = 20, min = 3) val first_name: String? = null,
    @field:NotBlank @field:Size(max = 20, min = 3) val last_name: String? = null,
    @field:NotEmpty val role: List< String?>? = null

){
    
    
}

only class:

    class AuthorInput {
    @NotBlank
    @Size(max = 20, min = 3)
    val first_name: String? = null

    @NotBlank
    @Size(max = 20, min = 3)
    val last_name: String? = null

    @NotEmpty
    val role: List<@NotBlank String?>? = null


}

I think you are just missing @Validated annotation on top of your controller class.

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.validation.annotation.Validated
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.NotEmpty

@SpringBootApplication
class BootValidationApplication

fun main(args: Array<String>) {
    runApplication<BootValidationApplication>(*args)
}

@RestController
@RequestMapping("/api/v1")
@Validated
class CustomerController {
    @PostMapping("/customer")
    fun createCustomer(@Valid @RequestBody customer: Customer) {
        println(customer)
    }
}

data class Customer(@field:NotEmpty val name: String)

output: 在此处输入图像描述

The issue has been fixed in Kotlin version 1.6.10. https://github.com/JetBrains/kotlin/releases/tag/v1.6.10 . After upgrading the exception is now org.springframework.web.bind.MethodArgumentNotValidException .

Make the field nullable even its not, and then add @field:NotNull

data class Test(@field:NotNull val id: String?)

This will stop kotlin validation to happen before javax

You have to add @get:Valid before. For example:

data class Location(
    @get:Valid @get:DecimalMin("-90") @get:DecimalMax("90")  val lat: Double,
    @get:Valid @get:DecimalMin("-180") @get:DecimalMax("180") val lng: Double
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM