Get response status code using Retrofit 2.0 and RxJava

60,856

Solution 1

Instead of declaring the API call like you did:

Observable<MyResponseObject> apiCall(@Body body);

You can also declare it like this:

Observable<Response<MyResponseObject>> apiCall(@Body body);

You will then have a Subscriber like the following:

new Subscriber<Response<StartupResponse>>() {
    @Override
    public void onCompleted() {}

    @Override
    public void onError(Throwable e) {
        Timber.e(e, "onError: %", e.toString());

        // network errors, e. g. UnknownHostException, will end up here
    }

    @Override
    public void onNext(Response<StartupResponse> startupResponseResponse) {
        Timber.d("onNext: %s", startupResponseResponse.code());

        // HTTP errors, e. g. 404, will end up here!
    }
}

So, server responses with an error code will also be delivered to onNext and you can get the code by calling reponse.code().

http://square.github.io/retrofit/2.x/retrofit/retrofit/Response.html

EDIT: OK, I finally got around to looking into what e-nouri said in their comment, namely that only 2xx codes will to to onNext. Turns out we are both right:

If the call is declared like this:

Observable<Response<MyResponseObject>> apiCall(@Body body);

or even this

Observable<Response<ResponseBody>> apiCall(@Body body);

all responses will end up in onNext, regardless of their error code. This is possible because everything is wrapped in a Response object by Retrofit.

If, on the other hand, the call is declared like this:

Observable<MyResponseObject> apiCall(@Body body);

or this

Observable<ResponseBody> apiCall(@Body body);

indeed only the 2xx responses will go to onNext. Everything else will be wrapped in an HttpException and sent to onError. Which also makes sense, because without the Response wrapper, what should be emitted to onNext? Given that the request was not successful the only sensible thing to emit would be null...

Solution 2

Inside onError method put this to get the code

((HttpException) e).code()

Solution 3

You should note that as of Retrofit2 all responses with code 2xx will be called from onNext() callback and the rest of HTTP codes like 4xx, 5xx will be called on the onError() callback, using Kotlin I've came up with something like this in the onError() :

mViewReference?.get()?.onMediaFetchFinished(downloadArg)
  if (it is HttpException) {
    val errorCode = it.code()
    mViewReference?.get()?.onMediaFetchFailed(downloadArg,when(errorCode){
      HttpURLConnection.HTTP_NOT_FOUND -> R.string.check_is_private
      else -> ErrorHandler.parseError(it)
    })
  } else {
    mViewReference?.get()?.onMediaFetchFailed(downloadArg, ErrorHandler.parseError(it))
  }
Share:
60,856
Admin
Author by

Admin

Updated on March 06, 2020

Comments

  • Admin
    Admin about 4 years

    I'm trying to upgrade to Retrofit 2.0 and add RxJava in my android project. I'm making an api call and want to retrieve the error code in case of an error response from the server.

    Observable<MyResponseObject> apiCall(@Body body);
    

    And in the RxJava call:

    myRetrofitObject.apiCall(body).subscribe(new Subscriber<MyResponseObject>() {
            @Override
            public void onCompleted() {
    
            }
    
            @Override
            public void onError(Throwable e) {
    
            }
    
            @Override
            public void onNext(MyResponseObject myResponseObject) {
               //On response from server
            }
        });
    

    In Retrofit 1.9, the RetrofitError still existed and we could get the status by doing:

    error.getResponse().getStatus()
    

    How do you do this with Retrofit 2.0 using RxJava?

  • e-nouri
    e-nouri over 8 years
    For people looking for the HTTP codes 4xx they will end up as HttpException in onError. the onNext will have the 2xx only.
  • david.mihola
    david.mihola over 8 years
    That's interesting... I just checked again (gist.github.com/DavidMihola/17a6ea373b9312fb723b) and all the codes I tried end up in onNext including 404, etc. I used Retrofit v2.0.0-beta3.
  • david.mihola
    david.mihola about 8 years
    @e-nouri: I just added a paragraph to my answer that takes your comment into account!
  • GabrielOshiro
    GabrielOshiro over 7 years
    This might be one of the most underrated answer I have ever seen in SO. This is genius. You can do everything you might need to do with the response with HttpException. I am using RxJava and I needed to parse the response after receiving a HTTP 400 BAD REQUEST. Thank you very much!
  • Ram Mandal
    Ram Mandal over 7 years
    how to handle if the response structure is different than the POJO class . I am stuck with this
  • Honghe.Wu
    Honghe.Wu over 7 years
    @david.mihola If Build Retrofit via add GsonConverterFactory, just as Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()), How to get the original response?
  • Benjamin Mesing
    Benjamin Mesing about 7 years
    Just make sure to check if it is an instanceof HttpException before because NetworkErrors will be IOExceptions and do not have a status code.
  • Mark
    Mark about 7 years
    I don't really see the point of Observable<Response<MyResponseObject>> apiCall(@Body body); - as per the edit you made, to me, it makes no sense to handle error checking when onNext() is called kind of goes against the Rx Philosophy - you'll just add complexity of having to check errors in onNext() each time surely?
  • Mark
    Mark about 7 years
    To follow on from @BenjaminMesing said about NetworkError, if you are doing more downstream operators (map / flatMap / forEach etc..) in your Rx before you subscribe then there could be a host of possible exception types, and not necessarily an error on the network request.
  • david.mihola
    david.mihola about 7 years
    Apart from error handling I think you might want to be able to inspect any response headers that came along with the body - that's also only possible if you get the Response. I rarely use it too - but I think it's good to know that it does exist.
  • Someone Somewhere
    Someone Somewhere over 6 years
    java.lang.ClassCastException: java.lang.Throwable cannot be cast to retrofit2.HttpException