Ya sé cómo recibir un objeto JSON y deserializarlo automáticamente en el formato requerido (por ejemplo, con una clase de datos). También mire aquí: ¿Cómo recibir un objeto JSON en Ktor?
Mi problema ahora es que quiero validar la solicitud JSON y devolver BadRequest si no está en el formato deseado, algo así en Django: https://stackoverflow.com/a/44085405/5005715
¿Cómo puedo hacer eso en Ktor/Kotlin? Desafortunadamente, no pude encontrar una solución en los documentos. Además, los campos obligatorios/opcionales estarían bien.
Puede usar hibernate-validator para validaciones de entrada. Consulte a continuación:
Agregar dependencia (Gradle):
compile "org.hibernate.validator:hibernate-validator:6.1.1.Final"Anote su clase de datos (DTO):
data class SampleDto( @field:Min(value=100) val id: Int, @field:Max(value=99) val age: Int )Agregar validador en enrutamiento:
import javax.validation.Validation fun Application.module() { val service = SampleService() val validator = Validation.buildDefaultValidatorFactory().validator routing { post("/sample/resource/") { val sampleDto = call.receive<SampleDto>() sampleDto.validate(validator) service.process(sampleDto) call.respond(HttpStatusCode.OK) } } } @Throws(BadRequestException::class) fun <T : Any> T.validate(validator: Validator) { validator.validate(this) .takeIf { it.isNotEmpty() } ?.let { throw BadRequestException(it.first().messageWithFieldName()) } } fun <T : Any> ConstraintViolation<T>.messageWithFieldName() = "${this.propertyPath} ${this.message}"Paso de bonificación (opcional): Agregar controlador de excepciones:
fun Application.exceptionHandler() { install(StatusPages) { exception<BadRequestException> { e -> call.respond(HttpStatusCode.BadRequest, ErrorDto(e.message, HttpStatusCode.BadRequest.value)) throw e } } } data class ErrorDto(val message: String, val errorCode: Int)Aquí hay un ejemplo rápido de cómo validar y responder con 400 si es necesario.
fun main(args: Array<String>) { embeddedServer(Netty, 5000) { install(CallLogging) install(ContentNegotiation) { gson { } } install(Routing) { post("test") { val sample = call.receive<Sample>() if (!sample.validate()) { call.respond(HttpStatusCode.BadRequest, "Sample did not pass validation") } call.respond("Ok") } } }.start() } fun Sample.validate(): Boolean = id > 5 data class Sample(val id: Int)¿Tenías algo más en mente?
No hay anotaciones incorporadas o similares.
Llevando la respuesta de Andreas un paso más allá, puede devolver una lista de errores cuando la solicitud no es válida, así:
post { val postDog = call.receive<PostDog>() val validationErrors = postDog.validate() if (validationErrors.isEmpty()) { // Save to database } else { call.respond(HttpStatusCode.BadRequest, validationErrors) } } fun PostDog.validate() : List<Error> { var validationErrors : MutableList<Error> = mutableListOf() if(name == null || name.isBlank()) validationErrors.add(Error(code = "dog.name.required", message = "Dog requires a name")) if(color == null || color.isBlank()) validationErrors.add(Error(code = "dog.color.required", message = "Dog requires a color")) return validationErrors } data class PostDog( val name: String, val color: String ) data class Error( val code : String, val message : String )