Home > Software engineering >  Ktor-server RequestValidation not working
Ktor-server RequestValidation not working

Time:09-15

I am using ktor (2.1.0) to create a small API. While doing so, I am trying to leverage Ktor-server cool functionalities, including RequestValidation.

That being said, it is not working and I can't figure out why since it looks to me very close to the examples in the documentation.

This is my server config:

embeddedServer(Netty, port = 8080) {

    routing {
        post("/stock") {
           val dto = call.receive<CreateStockRequest>()
            call.respond(HttpStatusCode.NoContent)
        }
    }

    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
        })
    }

    install(StatusPages) {
        exception<RequestValidationException> { call, cause ->
            call.respond(HttpStatusCode.BadRequest, cause.reasons.joinToString())
        }
    }

    install(RequestValidation) {
        validate<CreateStockRequest> { request ->
            if (request.name.isBlank())
                ValidationResult.Invalid("Stock must have a name")

            if (request.symbol.isBlank())
                ValidationResult.Invalid("Symbol must have a name")

            ValidationResult.Valid
        }
    }
}

This is the request object being "received":

@Serializable
data class CreateStockRequest(val name: String, val symbol: String)

And this is the body being sent:

{
    "name": "",
    "symbol": "something"
}

I was expecting to get a BadRequest response, but I am getting a NoContent response as if everything was fine with the request.

Am I doing something wrong?

CodePudding user response:

In your example, the server responds with BadRequest only if both "name" and "symbol" are blank. You can replace your validation logic with the when expression:

validate<CreateStockRequest> { request ->
    when {
        request.name.isBlank() -> ValidationResult.Invalid("Stock must have a name")
        request.symbol.isBlank() -> ValidationResult.Invalid("Symbol must have a name")
        else -> ValidationResult.Valid
    }
}

CodePudding user response:

I found the accepted answer to be a cleaner code solution, and that's why it is the accepted one.

That being said, the actual problem I was having is better explained by understanding that the compiler was getting confused with scopes. On each "if" clause, I needed to return the validation result, but the compiler was not having it.

The reason for that was the fact that it got confused with the context to which my return statement referred. My Solution was to use the '@' notation to specify the scope for the return:

validate<CreateStockRequest> { request ->
        if (request.name.isBlank())
            return@validate ValidationResult.Invalid("Stock must have a name")

        if (request.symbol.isBlank())
            return@validate ValidationResult.Invalid("Symbol must have a name")

        return@validate ValidationResult.Valid
    }
}
  • Related