Home > database >  Validate Request in Ktor
Validate Request in Ktor

Time:09-22

I have an API maked with Ktor and when som field of the request failed, it returns 500 error and I want to check all request data and return, in this case, 422.

Request class:

@Serializable
data class LoginRequest (
    val email: String,
    val password: String
)

Routing

route("v1/auth/login") {
    post {
        val loginRequest = call.receive<LoginRequest>()
        //LOGIN METHOD
    }
}

The error that now Ktor shows is:

[eventLoopGroupProxy-4-1] ERROR Application - Unhandled: POST - /v1/auth/login
kotlinx.serialization.MissingFieldException: Field 'password' is required for type with serial name

What is the best way to ensure that the system does not fail and respond with a BadRequest?

CodePudding user response:

If you wanna catch an exception in a specific place, you can use try/catch:

try {
    val loginRequest = call.receive<LoginRequest>()
    ...
} catch (e: SerializationException) {
    // serialization exceptions
    call.respond(HttpStatusCode.UnprocessableEntity)
} catch (t: Throwable) {
    // other exceptions
    call.respond(HttpStatusCode.InternalServerError)
}

If you wanna some global try/catch, Ktor has StatusPages feature for such case: it'll catch all exceptions during calls processing.

Same as with try/catch, you can catch a specific exception, like SerializationException, or use Exception/Throwable for any other exception.

install(StatusPages) {
    exception<SerializationException> { cause ->
        // serialization exceptions
        call.respond(HttpStatusCode.UnprocessableEntity)
    }
    exception<Throwable> { cause ->
        // other exceptions
        call.respond(HttpStatusCode.InternalServerError)
    }
}

CodePudding user response:

You can make fields nullable with the default null value, ignore errors when unknown properties are encountered and validate the result object manually. Here is an example:

import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.serialization.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class LoginRequest (
    val email: String? = null,
    val password: String? = null
)

suspend fun main() {
    embeddedServer(Netty, port = 8080) {
        install(ContentNegotiation) {
            json(Json {
                ignoreUnknownKeys = true
            })
        }

        routing {
            post("/") {
                val request = call.receive<LoginRequest>()

                if (request.email == null || request.password == null) {
                    call.respond(HttpStatusCode.UnprocessableEntity)
                    return@post
                }

                call.respond(HttpStatusCode.OK)
            }
        }
    }.start()
}
  • Related