处理 HttpClient Ktor 中的异常

and*_*max 20 kotlin ktor kotlin-multiplatform

我在公共模块中编写了如下公共代码,并在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?")
}

Run Code Online (Sandbox Code Playgroud)

但我的代码以 client.post 结尾,在没有网络的情况下出现取消的 corutineException 。我该如何处理这个异常以及任何其他异常?如果有互联网连接。没有任何失败,我希望能够处理异常。如何?

注意:try、catch 不起作用

Rah*_*ani 15

我们可以使用这个包装成功和错误情况的扩展函数以集中的方式处理它。我们可能会遇到网络错误、序列化错误或服务器错误,这些错误可能会暴露 UI 和 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
    }

Run Code Online (Sandbox Code Playgroud)

这是ApiResponse下面的定义,因此有工作代码:

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>()
    }
}
Run Code Online (Sandbox Code Playgroud)

我发现ApiResponse它非常适合我的情况,但您不一定必须对 api 响应进行建模,因为如果您有其他/特殊情况,您也可以使用kotlin-resultArrow 的 Either


HJC*_*Cee 9

没有对当前答案添加太多内容,但为了响应 CVS 的评论,我一直在使用以下内容在我的应用程序中添加 ktor 客户端错误处理。它使用结果API。runCatching {}捕获所有Throwables,您可以调整块的行为getOrElse以捕获您感兴趣的异常。

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
        }
    }
)
Run Code Online (Sandbox Code Playgroud)

我确信它可以变得更整洁,但这是迄今为止我想到的最好的。


and*_*max 7

经过到处询问后,我从github issues得到了帮助,并开始了这项工作

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")
}
Run Code Online (Sandbox Code Playgroud)

不要与catch (c: Throwable)混淆catch (e: Exception)

希望这可以帮助

  • 如何区分网络故障(可以向用户显示,但可以忽略)与指示某些内容损坏的错误(例如使用“KotlinxSerializer”进行响应时的反序列化错误)并应发送到崩溃报告工具? (2认同)