Обработка исключений в 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 {}
ловит все
Throwable
s, и вы можете настроить поведение
getOrElse
block для захвата интересующих вас исключений.
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")
}
}