You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加OkHttp网络拦截器后Retrofit异步API调用异常求助

Retrofit+Either异步调用异常问题修复方案

核心问题定位

  1. Retrofit配置冲突:你在provideSupportRetrofit里同时设置了callFactory(client)和client(client1),Retrofit中这两个配置互斥,callFactory会覆盖client的设置,导致你加的拦截器根本没生效,这就是Postman正常但代码异常的核心原因之一。
  2. 拦截器异常响应格式错误:捕获异常后返回的"{${e}}"不是合法JSON,Either转换器解析时会报错。
  3. Either适配器未处理非2xx响应:当前的EitherCall.enqueue只处理了onFailure,但拦截器返回的自定义999状态码属于onResponse范畴,没被正确转换成Either.Left。

分步修复

1. 修复Retrofit配置冲突

把自定义拦截器合并到已有OAUTH client中,去掉重复的callFactory设置:

@Singleton
@Provides
@BaseUrl(BaseUrlType.SERVICES)
fun provideSupportRetrofit(
    jsonConverters: Converter.Factory,
    @HttpClient(ClientType.OAUTH) oauthClient: OkHttpClient
): ServicesFactory {
    // 给已有OAUTH客户端添加自定义拦截器
    val clientWithInterceptor = oauthClient.newBuilder()
        .addInterceptor(YourInterceptor())
        .build()

    return fakeServicesFactory ?: Retrofit.Builder()
        .client(clientWithInterceptor) // 只保留client配置,内部自动处理callFactory
        .baseUrl(baseUrlServices)
        .addCallAdapterFactory(EitherCallAdapterFactory())
        .addConverterFactory(EitherConverterFactory())
        .addConverterFactory(FiberErrorConverterFactory())
        .addConverterFactory(jsonConverters)
        .addConverterFactory(primitiveTypeConverters)
        .build()
        .asFactory
}

2. 修正拦截器的异常响应格式

把异常响应改成标准JSON结构,同时处理ResponseBody的空安全:

override fun intercept(chain: Interceptor.Chain): Response {
    val request = chain.request()
    try {
        val response = chain.proceed(request)
        // 处理ResponseBody空安全,避免NPE
        val bodyString = response.body?.string() ?: ""
        return response.newBuilder()
            .body(ResponseBody.create(response.body?.contentType(), bodyString))
            .build()
    } catch (e: Exception) {
        e.printStackTrace()
        val msg = when (e) {
            is SocketTimeoutException -> "Timeout - Please check your internet connection"
            is UnknownHostException -> "Unable to make a connection. Please check your internet"
            is ConnectionShutdownException -> "Connection shutdown. Please check your internet"
            is IOException -> "Server is unreachable, please try again later."
            is IllegalStateException -> e.message ?: "Illegal state error"
            else -> e.message ?: "Unknown error"
        }
        // 生成合法的错误JSON
        val errorJson = """{"code":999,"message":"$msg","detail":"${e.toString()}"}"""
        return Response.Builder()
            .request(request)
            .protocol(Protocol.HTTP_1_1)
            .code(999)
            .message(msg)
            .body(ResponseBody.create(MediaType.parse("application/json"), errorJson))
            .build()
    }
}

3. 完善Either适配器的响应处理

让Either适配器同时处理成功响应、非成功响应和网络异常,统一转换成Either类型:

override fun enqueue(callback: Callback<Either<*, *>>) {
    call.enqueue(object : Callback<Any> {
        override fun onResponse(call: Call<Any>, response: Response<Any>) {
            if (response.isSuccessful) {
                // 成功响应包装成Either.Right
                callback.onResponse(this@EitherCall, Response.success(Either.Right(response.body())))
            } else {
                // 非成功响应解析错误体,包装成Either.Left
                val errorBody = response.errorBody()?.string()
                // 这里替换成你实际的错误模型解析逻辑
                val apiError = parseApiError(errorBody)
                callback.onResponse(this@EitherCall, Response.success(Either.Left(apiError)))
            }
        }

        override fun onFailure(call: Call<Any>, t: Throwable) {
            // 网络异常直接包装成Either.Left
            val networkError = ApiError(999, t.message ?: "Network error", t.toString())
            callback.onResponse(this@EitherCall, Response.success(Either.Left(networkError)))
        }
    })
}

注:parseApiError需要你实现,把错误JSON解析成自定义的ApiError数据类,确保和拦截器返回的JSON结构对应。

4. 验证拦截器生效

在拦截器的intercept方法开头加日志,确认请求是否经过拦截器:

Timber.d("Intercepting request: ${request.url()}")

优化建议

  • 加入OkHttp的LoggingInterceptor,打印完整的请求/响应日志,快速定位解析问题
  • 定义统一的ApiError数据类,让所有错误场景都返回这个类型,Either.Left统一持有该类型,避免类型混乱
  • 拦截器中只捕获IO相关异常(比如IOException及其子类),其他RuntimeException让上层处理,避免吞掉严重错误

内容的提问来源于stack exchange,提问作者Harsha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 07:36:18