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

如何使用GsonConverter全局处理Retrofit响应解析错误

解决Retrofit自定义Gson解析器无法获取响应URL的问题

你在使用RxJava、Retrofit和GsonConverterFactory全局处理响应解析错误时,自定义的GsonResponseBodyConverter无法获取请求URL,导致无法将错误关联到具体接口上报到Analytics服务。下面提供两种可行的实现方案:

方案一:通过Interceptor将URL存入响应头

实现步骤

  1. 自定义Interceptor,在响应返回时把请求URL写入响应头
  2. 在自定义Converter中从ResponseBody的头信息里读取URL

1. 编写URL拦截器

class UrlAddingInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = chain.proceed(request)
        // 自定义头字段存储请求URL,避免与现有头冲突
        return response.newBuilder()
            .addHeader("X-Request-Url", request.url.toString())
            .build()
    }
}

2. 给Retrofit添加该拦截器

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addInterceptor(UrlAddingInterceptor()) // 注意顺序,放在其他可能修改响应头的拦截器之前
    .addConverterFactory(GsonConverterFactory.create(gson))
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .build()

3. 修改自定义Converter获取URL

class GsonResponseBodyConverter<T>(
    private val gson: Gson,
    private val adapter: TypeAdapter<T>,
    private val analytics: Analytics
) : Converter<ResponseBody, T> {

    private val errorResponseSubject = PublishSubject.create<Pair<Throwable, String>>()

    init {
        val errorResponseFlowable = errorResponseSubject.toFlowable(BackpressureStrategy.BUFFER)

        val disposable = Flowables.combineLatest(
            errorResponseFlowable,
            Mapper::exceptionToEntity
        )
            .flatMap { analytics.saveError(it).andThen(Flowable.just(Unit)) }
            .subscribeOn(Schedulers.IO)
            .subscribe({
                print(it)
            }, RxThrowable.printStackTrace(logger))
    }

    @Throws(IOException::class)
    override fun convert(value: ResponseBody): T {
        val jsonReader = gson.newJsonReader(value.charStream())
        // 从响应头中读取URL,默认值处理避免空指针
        val requestUrl = value.headers().get("X-Request-Url") ?: "Unknown URL"

        return value.use {
            runCatching {
                val result = adapter.read(jsonReader)
                if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
                    throw JsonIOException("JSON document was not fully consumed.")
                }
                result
            }.getOrElse { error ->
                errorResponseSubject.onNext(Pair(error, requestUrl))
                throw error
            }
        }
    }
}

方案二:自定义CallAdapter统一处理错误(推荐)

通过自定义RxJava CallAdapter,在响应流阶段捕获错误,直接从Call或Response对象中获取URL,不需要修改Converter。

实现步骤

  1. 自定义CallAdapter包装类,处理错误并上报Analytics
  2. 编写CallAdapterFactory供Retrofit使用

1. 自定义CallAdapter包装类

class RxErrorHandlingCallAdapterWrapper<R>(
    private val delegate: CallAdapter<R, Flowable<R>>,
    private val analytics: Analytics
) : CallAdapter<R, Flowable<R>> {

    override fun responseType(): Type = delegate.responseType()

    override fun adapt(call: Call<R>): Flowable<R> {
        return delegate.adapt(call)
            .onErrorResumeNext { throwable ->
                // 统一处理所有流中的错误
                val requestUrl = when (throwable) {
                    // HTTP错误从Response中取URL
                    is HttpException -> throwable.response()?.raw()?.request?.url.toString() ?: "Unknown URL"
                    // 解析错误直接从Call的Request中取URL
                    is JsonIOException, is JsonSyntaxException -> call.request().url.toString() ?: "Unknown URL"
                    // 其他错误默认值
                    else -> "Unknown URL"
                }
                // 转换错误实体并上报
                val errorEntity = Mapper.exceptionToEntity(Pair(throwable, requestUrl))
                analytics.saveError(errorEntity)
                    .andThen(Flowable.error(throwable)) // 上报后继续抛出错误,不影响业务逻辑
            }
    }
}

2. 编写CallAdapterFactory

class RxErrorHandlingCallAdapterFactory(private val analytics: Analytics) : CallAdapter.Factory() {
    override fun get(
        returnType: Type,
        annotations: Array<Annotation>,
        retrofit: Retrofit
    ): CallAdapter<*, *>? {
        // 获取Retrofit默认的RxJava CallAdapter
        val delegate = retrofit.nextCallAdapter(this, returnType, annotations) as? CallAdapter<*, Flowable<*>>
        return delegate?.let { RxErrorHandlingCallAdapterWrapper(it, analytics) }
    }
}

3. 替换Retrofit的CallAdapterFactory

val retrofit = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    // 替换原有的RxJavaCallAdapterFactory
    .addCallAdapterFactory(RxErrorHandlingCallAdapterFactory(analytics))
    .build()

方案对比

  • 方案一:侵入性低,适合需要在Converter层处理解析逻辑的场景,但依赖响应头传递URL,需注意拦截器顺序。
  • 方案二:更符合Retrofit的设计理念,统一处理所有RxJava流错误,无需修改Converter,推荐用于全局错误上报场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 14:06:24