Ktor Client无法捕获ConnectException,求异常拦截方案
解决Ktor Client无法捕获java.net.ConnectException的方案
你的问题核心在于协程环境下未正确处理跨线程抛出的异常,以及原代码中runCatching { ... }.getOrElse { throw it }的写法等于直接重新抛出异常,没有起到捕获作用。以下是几种可行的异常拦截方案:
方案一:在协程作用域内添加全局异常处理器
为协程指定CoroutineExceptionHandler,可以捕获所有协程内未被局部try-catch处理的异常,包括ConnectException:
// 定义全局异常处理器 val requestExceptionHandler = CoroutineExceptionHandler { _, throwable -> when (throwable) { is java.net.ConnectException -> { // 处理连接异常,比如更新UI提示用户网络问题 } is RedirectResponseException, is ClientRequestException, is ServerResponseException -> { // 处理HTTP状态码异常 } else -> { // 处理其他未知异常 } } } // 在带异常处理器的协程中发起请求 CoroutineScope(Dispatchers.IO + requestExceptionHandler).launch { try { val response = client.post("$BASE_URL/api/users/login") val result = response.body() // 处理成功响应 } catch (e: RedirectResponseException) { Result.Error(e, e.response.status.description) } catch (e: ClientRequestException) { Result.Error(e, e.response.status.description) } catch (e: ServerResponseException) { Result.Error(e, e.response.status.description) } // 注意:ConnectException会被全局处理器捕获,无需在这里额外catch }
方案二:通过HttpClient拦截器统一捕获发送阶段异常
在HttpClient初始化时安装HttpSend拦截器,在请求发送环节直接捕获ConnectException:
val client = HttpClient(Android) { install(Logging) { level = LogLevel.ALL } install(ContentNegotiation) { json(Json { prettyPrint = true isLenient = true ignoreUnknownKeys = true }) } // 安装HttpSend拦截器,捕获请求发送时的异常 install(HttpSend) { intercept { request -> runCatching { execute(request) }.getOrElse { throwable -> when (throwable) { is java.net.ConnectException -> { // 可以在这里直接处理异常,或者重新抛出让上层协程捕获 // 例如返回一个自定义错误响应: HttpResponse(request, HttpStatusCode.ServiceUnavailable, headersOf(), ByteReadChannel.Empty) // 或者重新抛出:throw throwable } else -> throw throwable } } } } }
方案三:用runCatching直接包裹请求并处理结果
移除原代码中无效的runCatching + getOrElse组合,直接用runCatching处理整个请求流程:
CoroutineScope(Dispatchers.IO).launch { val result = runCatching { client.post("$BASE_URL/api/users/login") }.fold( onSuccess = { response -> Result.Success(response.body()) }, onFailure = { throwable -> when (throwable) { is RedirectResponseException -> Result.Error(throwable, throwable.response.status.description) is ClientRequestException -> Result.Error(throwable, throwable.response.status.description) is ServerResponseException -> Result.Error(throwable, throwable.response.status.description) is java.net.ConnectException -> Result.Error(throwable, throwable.message.orEmpty()) else -> Result.Error(throwable, throwable.message.orEmpty()) } } ) // 处理最终的Result对象 }
关键注意事项
- 必须在协程内执行Ktor请求:Ktor的
post等方法是挂起函数,必须在协程作用域(如CoroutineScope、viewModelScope等)内调用,否则会导致异常直接抛出无法捕获。 - 避免无效的异常重抛:原代码中
runCatching { ... }.getOrElse { throw it }会把捕获到的异常重新抛出,等同于没处理,直接删除这个逻辑即可。
内容的提问来源于stack exchange,提问作者Siavash
相关产品推荐
相关产品推荐

