添加OkHttp网络拦截器后Retrofit异步API调用异常求助
Retrofit+Either异步调用异常问题修复方案
核心问题定位
- Retrofit配置冲突:你在
provideSupportRetrofit里同时设置了callFactory(client)和client(client1),Retrofit中这两个配置互斥,callFactory会覆盖client的设置,导致你加的拦截器根本没生效,这就是Postman正常但代码异常的核心原因之一。 - 拦截器异常响应格式错误:捕获异常后返回的
"{${e}}"不是合法JSON,Either转换器解析时会报错。 - 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
相关产品推荐
相关产品推荐

