Ktorfit icon indicating copy to clipboard operation
Ktorfit copied to clipboard

Throw a HttpException when the request is sucessfull but the http status are not 2XX, like Retrofit

Open DevSrSouza opened this issue 8 months ago • 0 comments

Is your feature request related to a problem? Please describe. Using Retrofit, by default, when using suspend, when an API returns successfully (the backend actual responds), when the status code is not success (2XX) it throws a custom HttpException.

When working with Ktorfit and Ktor, when using Suspend and the direct type of the Response Body, when the server returns a 4XX with a Custom Body or Any Body at all, the Exception is ContentConvertException, this does not say nothing of what was the really cause of the error.

Describe the solution you'd like Creating a custom HttpException and throwing the Exception when the response is not 2XX like Retrofit does: https://github.com/square/retrofit/blob/8abb8af6ce730b137c2a9f95c3bb5164c93e955e/retrofit-adapters/scala/src/main/java/retrofit2/adapter/scala/BodyCallAdapter.java#L47-L51

Describe alternatives you've considered There are workarounds that I found that is not the best but is working so far for me that is creating a custom Converter.SuspendResponseConverter<HttpResponse, Any> that overrides the default IMPL of Ktorfit.

ktorfit {
    ....
    converterFactories(UnsuccessResponseConverterFactory())
}

public class KtorfitHttpException(
    @Transient public val response: HttpResponse,
    public val bodyText: String,
) : RuntimeException() {
    override val message: String
        get() = "HTTP " + response.status.value + " " + bodyText
}

internal class UnsuccessResponseConverterFactory : Converter.Factory {

   class UnsuccessResponseSuspendConverter(
        val typeData: TypeData,
        val ktorfit: Ktorfit
    ) : Converter.SuspendResponseConverter<HttpResponse, Any> {
        override suspend fun convert(response: HttpResponse): Any {
            return try {
                if (response.status.isSuccess()) {
                    response.call.body(typeData.typeInfo)
                } else {
                    throw KtorfitHttpException(response, response.bodyAsText())
                }
            } catch (exception: Exception) {
                throw exception
            }
        }
    }

    override fun suspendResponseConverter(
        typeData: TypeData,
        ktorfit: Ktorfit
    ): Converter.SuspendResponseConverter<HttpResponse, *>? {
        if (typeData.typeInfo.type != Response::class) {
            return UnsuccessResponseSuspendConverter(typeData, ktorfit)
        }
        return null
    }
}

DevSrSouza avatar Oct 10 '23 13:10 DevSrSouza