RxJava2/Retrofit2 - 处理 204 PUT 和 DELETE 请求的 null
RxJava2/Retrofit2 - Handling null for 204 PUT and DELETE requests
因此,我正在使用一个明确定义的 API 并且旨在不 return DELETE
和 PUT
操作的有效负载主体。
这在 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()
}
}
因此,我正在使用一个明确定义的 API 并且旨在不 return DELETE
和 PUT
操作的有效负载主体。
这在 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()
}
}