RxJava2/Retrofit2 - 处理 204 PUT 和 DELETE 请求的 null

RxJava2/Retrofit2 - Handling null for 204 PUT and DELETE requests

因此,我正在使用一个明确定义的 API 并且旨在不 return DELETEPUT 操作的有效负载主体。

这在 Rx 0.X 和 Rx 1.x 中是可以接受的。现在我正在更新到 Rx 2 并且在我应该如何处理空值方面存在生存危机。内容长度和正文当然为空,导致:

java.lang.NullPointerException: Null is not a valid element
   at io.reactivex.internal.queue.SpscLinkedArrayQueue.offer(SpscLinkedArrayQueue.java:68)
   at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.onNext(ObservableObserveOn.java:116)
   at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeOnObserver.onNext(ObservableSubscribeOn.java:63)

在 doOnNext 中。

我看到很多人建议 Optional<>,但出于用例原因,我也需要支持 Java7。我尝试向后移植,但无法完全正常工作。我也不想为他们的版本膨胀和导入 Guava 库。

我还注意到 flatMap 也可以帮助我处理这个与 map 相对的问题,我正在阅读差异。

目前我有一个非常粗糙的 OkHttp3 拦截器,它将检查状态,检查负载是否为空,并添加感觉很不对劲的虚拟内容。

我也试过添加转换工厂。

谁能提供建议并指导我正确的道路是什么?当然,API 可以更改,但 204 不应具有有效负载,因为它被定义为 HTTP 状态代码。

相关依赖项

compile('com.squareup.retrofit2:retrofit:2.1.0') {
     exclude module: 'okhttp'
} 
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
compile 'com.squareup.okhttp3:okhttp:3.5.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.5.0'

compile 'io.reactivex.rxjava2:rxjava:2.0.5'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.trello.rxlifecycle2:rxlifecycle:2.0.1'
compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.0.1'

您需要在Retrofit中声明您的请求方法,例如:

@DELETE(...)
Call<Void> deleteFile(...args);

在 RxJava 中,您的 Observable 必须输入:

@DELETE(...)
Observable<Response<Void>> deleteFile(...args);

onNext()doOnNext()中,如果请求成功,您将正常收到Response

具有Void不会将响应主体发送到转换器以进一步反序列化。所有 empty-response Call 都应键入 Void.

既然你有 RxJava2 和改造 2,你可以使用更易读的方式来描述你真正想要的端点使用 Completable

Completable 设计 return 只有 onComplete()onError(Exception) 所以你在看到它的那一刻就知道发生了什么(你只关心执行而不关心return 值)并且不知道 Response<Void>.

下会有什么

因此您的端点调用应如下所示:

@DELETE(...)
Completable deleteFile(...args);

支持更多类型,请参阅 Retrofit 2 RxJava 2 Adapter 页面。Single 是在 api 调用方面脱颖而出的一种。

对于仍在寻找答案的人:将 Void 替换为空 class 解决了我的问题。像这样:

class EmptyResponse {}

并且只使用这个 class 而不是 Void

根据下面的用例,可能是另一个 solution

可以用 Okhttp3 拦截器处理。

import okhttp3.Interceptor
import okhttp3.MediaType
import okhttp3.Response
import okhttp3.ResponseBody

object EmptyBodyInterceptor : Interceptor {

    override fun intercept(chain: Interceptor.Chain): Response {
        val response = chain.proceed(chain.request())
        if (response.isSuccessful.not() || response.code().let { it != 204 && it != 205 }) {
            return response
        }

        if ((response.body()?.contentLength() ?: -1) >= 0) {
            return response.newBuilder().code(200).build()
        }

        val emptyBody = ResponseBody.create(MediaType.get("text/plain"), "")

        return response
            .newBuilder()
            .code(200)
            .body(emptyBody)
            .build()
    }
}