Search code examples
rx-javaretrofit2okhttp

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


So, I am working with an API that is clearly defined and is designed to not return a payload body on DELETE and PUT operations.

This was acceptable in Rx 0.X and Rx 1.x. Now I'm updating to Rx 2 and having an existential crisis with how I should handle the null values. The content-length and body are of course null causing:

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)

in the doOnNext.

I've seen many people suggest Optional<> but I need to support Java7 as well for use case reasons. I attempted back-porting but I couldn't quite get it to work. I also don't want to bloat and import the Guava library for their version.

I also noticed flatMap may also help me handle this opposed to map and I'm reading up on the differences.

Currently I have a very crude, OkHttp3 Interceptor that will check the status, check if the payload is empty, and add dummy content which just feels so wrong.

I've also tried to add a convert factory.

Can anyone offer suggestions and guide me on what the proper path is? Sure, the API can change, but 204 isn't supposed to have a payload by virtue of it's definition as an HTTP status code.

Relevant Dependencies

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'

Solution

  • You need to declare your request method in Retrofit like:

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

    In RxJava your Observable has to be typed:

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

    In onNext() or doOnNext() you will receive the Response normally, if the request is successful.

    Having Void will not send the response body to the converter for further deserialization. All empty-response Calls should be typed as Void.