I already know how to receive a JSON object and automatically deserialize it into the required format (e.g. with a data class). Also look here: How to receive JSON object in Ktor?
My problem now is that I want to validate the JSON request and return BadRequest
if it's not in the desired format, something like that in Django: https://stackoverflow.com/a/44085405/5005715
How can I do that in Ktor/Kotlin? Unfortunately, I couldn't find a solution in the docs. Also, required/optional fields would be nice.
You can use hibernate-validator
for input validations. Refer below:
Add Dependency (Gradle):
compile "org.hibernate.validator:hibernate-validator:6.1.1.Final"
Annotate your data class (DTO):
data class SampleDto(
@field:Min(value=100)
val id: Int,
@field:Max(value=99)
val age: Int
)
Add Validator in Routing:
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}"
Bonus Step (Optional) - Add Exception Handler:
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)
Here is a quick example of how to validate and respond with 400 if needed.
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)
Did you have something else in mind?
There are no inbuilt annotations or the like.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With