Je sais déjà comment recevoir un objet JSON et le désérialiser automatiquement au format requis (par exemple avec une classe de données). Regardez également ici: Comment recevoir un objet JSON dans Ktor? p>
Mon problème maintenant est que je veux valider la requête JSON et renvoyer BadRequest si ce n'est pas dans le format souhaité, quelque chose comme ça dans Django: https://stackoverflow.com/a/44085405/5005715
Comment puis-je faire cela dans Ktor / Kotlin? Malheureusement, je n'ai pas trouvé de solution dans la documentation. De plus, les champs obligatoires / facultatifs seraient bien.
4 Réponses :
Voici un exemple rapide de la façon de valider et de répondre avec 400 si nécessaire.
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)
Aviez-vous autre chose en tête?
Il n'y a pas d'annotations intégrées ou similaire.
Je ne suis pas sûr que Ktor ait déjà quelque chose pour ça. Spring le gère bien avec @Valid annotation . Je cherchais aussi quelque chose comme ça pour valider que ce soit du json ou de l'objet. J'ai trouvé ce framework https://github.com/making/yavi . Ça a l'air intéressant. Je vais faire un essai
Vous pouvez utiliser hibernate-validator pour les validations d'entrée. Reportez-vous ci-dessous:
Ajouter une dépendance (Gradle):
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)
Annoter votre classe de données (DTO):
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}"
Ajouter un validateur dans le routage:
data class SampleDto(
@field:Min(value=100)
val id: Int,
@field:Max(value=99)
val age: Int
)
Étape bonus (facultative) - Ajouter un gestionnaire d'exceptions:
compile "org.hibernate.validator:hibernate-validator:6.1.1.Final"
En poussant plus loin la réponse d'Andreas, vous pouvez renvoyer une liste d'erreurs lorsque la requête est invalide comme ceci:
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
)
Quel est le problème avec la validation de l'objet après le décodage? BTW, l'exemple Django ressemble à JSR-303 , mais je n'ai pas vu quelque chose comme ça dans Ktor
le problème est que si un paramètre obligatoire est manquant, Jackson enverra une exception car il ne peut pas désérialiser le JSON. J'ai le même problème et je n'ai pas trouvé dans la documentation comment échouer avec une BadRequestException