Tengo bastantes proyectos que se están migrando lentamente de Java a Kotlin, pero tengo un problema al cambiar de Java POJO a las clases de datos de Kotlin. La validación de beans deja de funcionar en los controladores REST. Creé un proyecto muy simple directamente desde https://start.spring.io para demostrar la falla.
@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)y 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" } } El envío de una solicitud no activa la validación del bean, pero arroja una HttpMessageNotReadableException porque la id es NULL.
curl -X POST http://localhost:8080 -H "Content-Type: application/json" -d "{}" Probé tanto @Valid como @Validated y usé @get:NotNull en los atributos, pero nada funciona. Veo que muchos otros tienen el mismo problema y funciona el uso de una clase Java de un controlador Kotlin REST. También cambiar a una clase que no sea de datos hace que la validación funcione. ¿Alguien sabe si es posible hacer que la validación de beans funcione con las clases de datos de Kotlin?
El problema se solucionó en la versión 1.6.10 de Kotlin. https://github.com/JetBrains/kotlin/releases/tag/v1.6.10 . Después de actualizar, la excepción ahora es org.springframework.web.bind.MethodArgumentNotValidException .
Haga que el campo sea anulable incluso si no lo es, y luego agregue @field:NotNull
data class Test(@field:NotNull val id: String?)Esto detendrá la validación de kotlin antes de javax
Creo que solo te falta la anotación @Validated en la parte superior de tu clase de controlador.
import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.http.HttpHeaders import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.validation.FieldError import org.springframework.validation.annotation.Validated import org.springframework.web.bind.MethodArgumentNotValidException import org.springframework.web.bind.annotation.* import org.springframework.web.context.request.WebRequest import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler import java.time.LocalDateTime 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): ResponseEntity<Customer> { return ResponseEntity.ok(customer) } } @ControllerAdvice class CustomerExceptionHandler : ResponseEntityExceptionHandler() { override fun handleMethodArgumentNotValid( ex: MethodArgumentNotValidException, headers: HttpHeaders, status: HttpStatus, request: WebRequest ): ResponseEntity<Any> { val fieldErrors: List<FieldError> = ex.fieldErrors val errorMapping = fieldErrors.associate { it.field to it.defaultMessage } val errorDetails = ErrorDetails( timestamp = LocalDateTime.now(), message = "Validation Failed", details = errorMapping ) return ResponseEntity(errorDetails, HttpStatus.BAD_REQUEST) } } data class Customer( @field:NotEmpty(message = "Mandatory field: Name is missing in Request Body") val name: String ) data class ErrorDetails(val timestamp: LocalDateTime, val message: String, val details: Map<String, String?>)