In the Ktor Client, is there any reasonable way of...
# ktor
p
In the Ktor Client, is there any reasonable way of handling a
404
response as
null
without either losing the default response validation (i.e
expectSuccess = false
) or intercepting
ClientRequestException
?
a
Do you mean to return
null
when calling the
body
method with a nullable type?
p
That would be simplest - or be able to permit certain non-success statuses so one could handle them directly, but still raise exceptions for any unexpected statuses
a
You can modify the default response validation logic by defining another response validator with the code copied from the default one. Here is an example:
Copy code
val client = HttpClient(CIO) {
    HttpResponseValidator {
        validateResponse { response ->
            val statusCode = response.status.value
            val originCall = response.call

            if (statusCode == HttpStatusCode.NotFound.value) { // Skip 404
                return@validateResponse
            }

            val exceptionCall = if (!originCall.response.isSaved) {
                originCall.save()
            } else {
                originCall
            }

            val exceptionResponse = exceptionCall.response
            val exceptionResponseText = try {
                exceptionResponse.bodyAsText()
            } catch (_: MalformedInputException) {
                "<body failed decoding>"
            }
            val exception = when (statusCode) {
                in 300..399 -> RedirectResponseException(exceptionResponse, exceptionResponseText)
                in 400..499 -> ClientRequestException(exceptionResponse, exceptionResponseText)
                in 500..599 -> ServerResponseException(exceptionResponse, exceptionResponseText)
                else -> ResponseException(exceptionResponse, exceptionResponseText)
            }
            throw exception
        }
    }
}
p
that's effectively what we've ended up with (albeit with the validation logic wrapped in a
.requireSuccess()
extension rather than the plugin along with a
.onStatus
extension) allowing for this kind of usage:
Copy code
return get("api/config/id") { url.appendPathSegments("$configId") }
        .onStatus(NotFound) { return null }
        .requireSuccess()
        .body<T>()