Search code examples
kotlinktorkotlin-multiplatformktor-client

Parsing an error body in a ktor HTTPClient


I have an api that returns error body with the correct error information when a bad request is sent. For eg I get status code 400 and the following body -

{
  "errorCode": 1011,
  "errorMessage": "Unable to get Child information"
}

Now when I am writing a ktor client in a multi-platform module for this, I catch this in a response validator like -

 HttpResponseValidator {
            validateResponse {
                val statusCode = it.status.value
                when (statusCode) {
                    in 300..399 -> print(it.content.toString())
                    in 400..499 -> {
                        print(it.content.toString())
                        throw ClientRequestException(it)
                    }
                    in 500..599 -> print(it.content.toString())
                }
            }
            handleResponseException {
                print(it.message)
            }
        }

My query here is I am not able to access the response error body in either validateResponse or handleResponseException. Is there a way i can catch and parse that to get the actual error sent by server?


Solution

  • You can declare a data class Error to represent the error response you expect.

    import kotlinx.serialization.Serializable
    
    @Serializable
    data class Error(
        val errorCode: Int,   //if by errorCode you mean the http status code is not really necessary to include here as you already know it from the validateResponse
        val errorMessage: String
    )
    

    you can have a suspend fun to parse the response and have it as an instance of the Error data class

     suspend fun getError(responseContent: ByteReadChannel): Error {
        responseContent.readUTF8Line()?.let {
            return Json(JsonConfiguration.Stable).parse(Error.serializer(), it)
        }
        throw IllegalArgumentException("not a parsable error")
    }
    

    then inside the handleResponseException

    handleResponseException { cause -> 
                val error = when (cause) {
                    is ClientRequestException -> exceptionHandler.getError(cause.response.content)
    // other cases here 
    
                    else -> // throw Exception() do whatever you need 
                }
    //resume with the error 
            }
    

    you can implement some logic based on the error you get to throw an exception and catch it somewhere else in your code for example

    when (error.errorCode) {
            1-> throw MyCustomException(error.errorMessage)
            else -> throw Exception(error.errorMessage)
        }
    

    I hope it helps