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

Retrofit2结合RxJava Observable处理HTTP 204无内容响应问题

解决Retrofit2+RxJava处理204 No Content响应的异常问题

嘿,这个问题我太熟了!我之前用Retrofit+RxJava处理类似的204场景时也踩过同样的坑。核心原因是:你的接口声明返回Observable<List<User>>,但当服务器返回204(无内容)时,GsonConverterFactory/ScalarsConverterFactory会尝试解析空响应体,可空内容根本没法转成List<User>,所以直接抛出异常走到onFail里了。

下面给你两种实用的解决方案,按需选择:

方案一:改用Response<T>包装返回类型(推荐,简单直接)

这种方法让你能手动控制所有响应状态码的处理逻辑,不用修改全局转换器配置。

第一步:修改Retrofit接口

把返回类型改成Observable<Response<List<User>>>,Response是Retrofit提供的类,能包含状态码、响应体等完整信息:

@GET("/user/{topic}")
fun getAllUserFor(@Path(value="topic", encoded=true) topic: String): Observable<Response<List<User>>>

第二步:修改调用逻辑

在订阅时判断响应状态码,204时直接返回空列表给onSuccess,其他正常状态或异常按逻辑处理:

fun getAllUsers(topic: String, onSuccess: Consumer<List<User>>, onFail: Consumer<Throwable>) {
    val api = NetworkLayer.getUserApi()
    api.getAllUserFor(topic)
        .observeOn(AndroidSchedulers.mainThread())
        .subscribeOn(Schedulers.io())
        .subscribe({ response ->
            if (response.isSuccessful) {
                when (response.code()) {
                    200 -> {
                        // 200响应正常解析用户列表
                        response.body()?.let(onSuccess::accept) 
                            ?: onFail.accept(IllegalStateException("200响应但无内容"))
                    }
                    204 -> {
                        // 204无内容,返回空列表给成功回调
                        onSuccess.accept(emptyList())
                    }
                    else -> {
                        // 其他成功状态码(比如201)按需处理,这里默认抛异常
                        onFail.accept(HttpException(response))
                    }
                }
            } else {
                // 非成功状态码(4xx/5xx)抛出HttpException
                onFail.accept(HttpException(response))
            }
        }, onFail)
}

方案二:自定义Converter全局处理204场景

如果你的项目里很多接口都有类似的204处理需求,可以自定义一个Converter包装GsonConverter,自动把204响应转成空列表。

示例代码(简化版)

class EmptyListGsonConverterFactory(private val gson: Gson) : Converter.Factory() {
    override fun responseBodyConverter(
        type: Type,
        annotations: Array<Annotation>,
        retrofit: Retrofit
    ): Converter<ResponseBody, *>? {
        val delegate = gson.getAdapter(TypeToken.get(type))
        return Converter { body ->
            if (body.contentLength() == 0L) {
                // 判断是否是List类型,是的话返回空列表
                if (type is ParameterizedType && type.rawType == List::class.java) {
                    emptyList<Any>()
                } else {
                    delegate.fromJson(body.charStream())
                }
            } else {
                delegate.fromJson(body.charStream())
            }
        }
    }

    companion object {
        fun create(): EmptyListGsonConverterFactory {
            return create(Gson())
        }

        fun create(gson: Gson): EmptyListGsonConverterFactory {
            return EmptyListGsonConverterFactory(gson)
        }
    }
}

然后在构建Retrofit时替换原来的GsonConverterFactory:

Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(EmptyListGsonConverterFactory.create())
    .addConverterFactory(ScalarsConverterFactory.create())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .build()

这种方式下,原来的接口和调用代码都不用改,204响应会自动被转成空列表,不会抛出异常。不过要注意这个Converter的逻辑要和你的接口返回类型匹配,避免出现类型转换错误。

关键提示

  • 为什么原来的代码会报错?因为Retrofit的转换器默认认为成功响应(2xx)一定有可解析的内容,204的空响应体打破了这个预期,所以抛出解析异常。
  • 方案一更灵活,能针对单个接口定制状态码处理;方案二更适合全局统一处理类似场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:20:39