【问题标题】:Ktor: How can I validate JSON request?Ktor:如何验证 JSON 请求?
【发布时间】:2019-06-09 18:42:57
【问题描述】:

我已经知道如何接收 JSON 对象并将其自动反序列化为所需的格式(例如,使用数据类)。也看这里:How to receive JSON object in Ktor?

我现在的问题是我想验证 JSON 请求并返回 BadRequest,如果它不是所需的格式,就像 Django 中的那样:https://stackoverflow.com/a/44085405/5005715

如何在 Ktor/Kotlin 中做到这一点?不幸的是,我在文档中找不到解决方案。此外,必填/可选字段会很好。

【问题讨论】:

  • 解码后验证对象有什么问题?顺便说一句,Django 示例看起来像 JSR-303,但我在 Ktor 中没有看到类似的东西
  • 问题是如果缺少一个强制参数,Jackson 会发送一个异常,因为它不能反序列化 JSON。我有同样的问题,我没有在文档中找到如何因 BadRequestException 而失败
  • @Rytek 你找到解决你问题的方法了吗?

标签: json kotlin ktor


【解决方案1】:

您可以使用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)

【讨论】:

  • 这太棒了!!!我需要额外的依赖:实现“org.glassfish:javax.el:3.0.1-b08”
【解决方案2】:

下面是一个快速示例,说明如何在需要时使用 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)

你还有别的想法吗?

没有内置注释或类似的东西。

【讨论】:

    【解决方案3】:

    进一步了解 Andreas 的答案,您可以在请求无效时返回错误列表,如下所示:

        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
        )
    

    【讨论】:

      【解决方案4】:

      我不确定 Ktor 是否已经为此提供了一些东西。Spring 使用 @Valid 注释 可以很好地处理它。 我也在寻找这样的东西来验证json还是对象。我找到了这个框架https://github.com/making/yavi。看起来很有趣。我会试试的

      【讨论】:

        【解决方案5】:

        使用这个库

        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()
                    }
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-05
          • 2017-10-18
          • 2023-01-21
          • 1970-01-01
          • 2020-08-21
          • 2010-12-25
          • 1970-01-01
          • 2010-11-11
          相关资源
          最近更新 更多