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
相关产品推荐
相关产品推荐

