Обработка исключений в HttpClient Ktor

Я написал общий код в общем модуле, как показано ниже, и протестирован в среде JS

val response = client.post<HttpResponse>(url) {
    body = TextContent("""{"a":1,"b":2}""", ContentType.Application.Json)
}
if (response.status != HttpStatusCode.OK) {
    logger.error("Error, this one failed bad?")
}

Но мой код заканчивается на client.post отмененной исключительной ситуацией corutineException в сети. Как мне справиться с этим и любым другим исключением? Если есть подключение к интернету. Ничего не выходит из строя, я хочу иметь возможность обрабатывать исключения. Как?

Примечание: попробуйте, поймать не работает

4 ответа

Решение

Хорошо после того, как спросить здесь и там, я получил помощь по вопросам GitHub и пришел к этой работе

try {
    val response = client.post<HttpResponse>(url) {
        body = TextContent("""{"a":1,"b":2}""", ContentType.Application.Json)
    }
    if (response.status != HttpStatusCode.OK) {
        logger.error("Error, this one failed bad?")
    }
} catch (cause: Throwable) {
    logger.error("Catch your error here")
}

не путай с catch (c: Throwable) с catch (e: Exception)

надеюсь это поможет

Мы можем справиться с этим централизованно, используя эту функцию расширения, которая охватывает случаи успеха и ошибки. У нас могут быть ошибки сети, ошибки сериализации или ошибки сервера, которые могут отображать тела/сообщения об ошибках для кода состояния пользовательского интерфейса и http.

      suspend inline fun <reified T, reified E> HttpClient.safeRequest(
    block: HttpRequestBuilder.() -> Unit,
): ApiResponse<T, E> =
    try {
        val response = request { block() }
        ApiResponse.Success(response.body())
    } catch (e: ClientRequestException) {
        ApiResponse.Error.HttpError(e.response.status.value, e.errorBody())
    } catch (e: ServerResponseException) {
        ApiResponse.Error.HttpError(e.response.status.value, e.errorBody())
    } catch (e: IOException) {
        ApiResponse.Error.NetworkError
    } catch (e: SerializationException) {
        ApiResponse.Error.SerializationError
    }

suspend inline fun <reified E> ResponseException.errorBody(): E? =
    try {
        response.body()
    } catch (e: SerializationException) {
        null
    }

Здесь определено ниже, так что есть рабочий код:

      sealed class ApiResponse<out T, out E> {
    /**
     * Represents successful network responses (2xx).
     */
    data class Success<T>(val body: T) : ApiResponse<T, Nothing>()

    sealed class Error<E> : ApiResponse<Nothing, E>() {
        /**
         * Represents server (50x) and client (40x) errors.
         */
        data class HttpError<E>(val code: Int, val errorBody: E?) : Error<E>()

        /**
         * Represent IOExceptions and connectivity issues.
         */
        object NetworkError : Error<Nothing>()

        /**
         * Represent SerializationExceptions.
         */
        object SerializationError : Error<Nothing>()
    }
}

я нашел ApiResponseхорошо работает в моем случае, но вам не обязательно моделировать ответы API, так как если у вас есть другие/особые случаи, вы также можете использовать kotlin-result или Arrow's Someone .

Не добавляя многого к текущему ответу, но в ответ на комментарий CVS я использовал следующее, чтобы добавить обработку ошибок клиента ktor в свои приложения. Он использует Result API. runCatching {}ловит все Throwables, и вы можете настроить поведение getOrElseblock для захвата интересующих вас исключений.

      suspend fun <T> HttpClient.requestAndCatch(
    block: suspend HttpClient.() -> T,
    errorHandler: suspend ResponseException.() -> T
): T = runCatching { block() }
    .getOrElse {
        when (it) {
            is ResponseException -> it.errorHandler()
            else -> throw it
        }
    }

// Example call
client.requestAndCatch(
    { get<String>("/") },
    {
        when (response.status) {
            HttpStatusCode.BadRequest -> {} // Throw errors or transform to T 
            HttpStatusCode.Conflict -> {}
            else -> throw this
        }
    }
)

Я уверен, что это можно было бы сделать более аккуратным, но это лучшее, что я придумал до сих пор.

Для тех, кто не хочет использовать, попробуйте catch

      override suspend fun requestGPT(model: RequestModel, result: (response: ResponseModel?, error: String?) -> Unit) {
    val httpResponse = client.post {
        headerGpt()
        header("Content-Type","application/json")
        setBody(model)
        url(Endpoints.requestGpt)
    }
    if (httpResponse.status.value in 200..299) {
        result(httpResponse.body<ResponseModel>(),null)
    }else{
        result(null,"Error Http Status Code --> ${httpResponse.status.value} \n ${httpResponse.bodyAsText()}")
    }
}

Использование будет похоже на

       repository.requestGPT(model){response, error ->
    response?.let {
        println("$response")
    }
    error?.let {
        println("$error")
    }
  }
Другие вопросы по тегам