Ktor: Как я могу проверить запрос JSON?

Я уже знаю, как получить объект JSON и автоматически десериализовать его в требуемый формат (например, с классом данных). Также посмотрите здесь: Как получить объект JSON в Ktor?

Моя проблема сейчас в том, что я хочу проверить запрос JSON и вернуть BadRequest, если он не в желаемом формате, что-то вроде этого в Django: https://stackoverflow.com/a/44085405/5005715

Как я могу это сделать в Ktor / Kotlin? К сожалению, я не смог найти решение в документации. Также было бы неплохо заполнить обязательные / необязательные поля.


person Aliquis    schedule 15.01.2019    source источник
comment
Что не так с проверкой объекта после декодирования? Кстати, пример Django выглядит как JSR-303, но я не видел ничего подобного в Ktor   -  person Omar Mainegra    schedule 16.01.2019
comment
проблема в том, что если обязательный параметр отсутствует, Джексон отправит исключение, потому что он не может десериализовать JSON. У меня такая же проблема, и я не нашел в документации, как выйти из строя с помощью BadRequestException   -  person Rytek    schedule 18.03.2020
comment
@Rytek вы нашли что-нибудь, что решило вашу проблему?   -  person VishalDevgire    schedule 06.04.2021


Ответы (5)


Вот быстрый пример того, как подтвердить и ответить 400, если это необходимо.

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)

Вы имели в виду что-то еще?

Нет встроенных аннотаций или чего-то подобного.

person avolkmann    schedule 16.01.2019

Вы можете использовать hibernate-validator для проверки ввода. См. Ниже:

Добавить зависимость (Gradle):

compile "org.hibernate.validator:hibernate-validator:6.1.1.Final"

Добавьте аннотации к вашему классу данных (DTO):

data class SampleDto(
    @field:Min(value=100)
    val id: Int,
    @field:Max(value=99)
    val age: Int
)

Добавить валидатор в маршрутизацию:

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}"

Бонусный шаг (необязательно) - добавление обработчика исключений:

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)
person Sahil Chhabra    schedule 18.01.2020
comment
Отлично!!! Мне нужна была дополнительная зависимость: реализация org.glassfish: javax.el: 3.0.1-b08 - person Arnau Miró Maestre; 17.03.2021

Взяв ответ Андреаса на шаг дальше, вы можете вернуть список ошибок, когда запрос недействителен, например:

    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
    )
person 34m0    schedule 22.03.2020

Не уверен, что у Ktor уже есть что-то для этого. Spring справляется с этим с помощью @Valid annotation. Я также искал что-то подобное, чтобы проверить, json или объект. Я нашел эту структуру https://github.com/making/yavi. Смотрится интересно. Я попробую

person Boris EKUE-HETTAH    schedule 29.03.2019

использовать эту библиотеку

https://github.com/valiktor/valiktor

data class Employee(val id: Int, val name: String, val email: String) {
        init {
            validate(this) {
                validate(Employee::id).isPositive()
                validate(Employee::name).hasSize(min = 3, max = 80)
                validate(Employee::email).isNotBlank().isEmail()
            }
        }
    }
person Mohammad Mohammadi    schedule 29.03.2021