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