Ottieni il codice di stato della risposta utilizzando Retrofit 2.0 e RxJava


108

Sto cercando di aggiornare a Retrofit 2.0 e aggiungere RxJava nel mio progetto Android. Sto effettuando una chiamata api e desidero recuperare il codice di errore in caso di risposta di errore dal server.

Observable<MyResponseObject> apiCall(@Body body);

E nella chiamata RxJava:

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, RetrofitError esisteva ancora e potevamo ottenere lo stato facendo:

error.getResponse().getStatus()

Come puoi farlo con Retrofit 2.0 usando RxJava?

Risposte:


185

Invece di dichiarare la chiamata API come hai fatto:

Observable<MyResponseObject> apiCall(@Body body);

Puoi anche dichiararlo in questo modo:

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

Avrai quindi un abbonato come il seguente:

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!
    }
}

Pertanto, verranno consegnate anche le risposte del server con un codice di errore onNexte sarà possibile ottenere il codice chiamando reponse.code().

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

EDIT: OK, finalmente sono riuscito a esaminare cosa ha detto e-nouri nel loro commento, vale a dire che solo i codici 2xx lo faranno onNext. Si scopre che abbiamo entrambi ragione:

Se la chiamata viene dichiarata in questo modo:

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

o anche questo

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

tutte le risposte finiranno in onNext, indipendentemente dal loro codice di errore. Questo è possibile perché tutto è avvolto in un Responseoggetto da Retrofit.

Se invece la chiamata viene dichiarata così:

Observable<MyResponseObject> apiCall(@Body body);

o questo

Observable<ResponseBody> apiCall(@Body body);

infatti solo le risposte 2xx andranno a onNext. Tutto il resto verrà avvolto in un HttpExceptione inviato a onError. Il che ha anche senso, perché senza il Responsewrapper, cosa dovrebbe essere emesso onNext? Dato che la richiesta non è andata a buon fine l'unica cosa sensata da emettere sarebbe null...


16
Per le persone che cercano i codici HTTP 4xx, finiranno come HttpException in onError. onNext avrà solo il 2xx.
e-nouri

2
Interessante ... Ho appena controllato di nuovo ( gist.github.com/DavidMihola/17a6ea373b9312fb723b ) e tutti i codici che ho provato finiscono per onNextincludere 404, ecc. Ho usato Retrofit v2.0.0-beta3.
david.mihola

@ e-nouri: ho appena aggiunto un paragrafo alla mia risposta che tiene conto del tuo commento!
david.mihola

1
@ 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?
Honghe

1
A parte la gestione degli errori, penso che potresti voler essere in grado di ispezionare tutte le intestazioni di risposta fornite con il corpo - anche questo è possibile solo se ottieni il file Response. Anche io lo uso raramente, ma penso che sia bello sapere che esiste.
david.mihola

112

All'interno del metodo onError inserire questo per ottenere il codice

((HttpException) e).code()

7
Questa potrebbe essere una delle risposte più sottovalutate che abbia mai visto in SO. Questo è geniale. Puoi fare tutto ciò di cui potresti aver bisogno con la risposta HttpException. Sto usando RxJava e avevo bisogno di analizzare la risposta dopo aver ricevuto un file HTTP 400 BAD REQUEST. Grazie mille!
GabrielOshiro

29
Assicurati solo di controllare se si tratta di un'istanza di HttpException prima perché NetworkErrors sarà IOExceptions e non avrà un codice di stato.
Benjamin Mesing,

Per seguire @BenjaminMesing ha detto di NetworkError, se stai facendo più operatori a valle (map / flatMap / forEach ecc ..) nella tua Rx prima di iscriverti, potrebbero esserci una serie di possibili tipi di eccezione, e non necessariamente un errore su la richiesta di rete.
Mark Keen

java.lang.ClassCastException: java.lang.Throwable non può essere cast per retrofit2.HttpException
Someone Somewhere

7

Dovresti notare che a partire da Retrofit2 tutte le risposte con codice 2xx verranno chiamate dal callback onNext () e il resto dei codici HTTP come 4xx, 5xx verrà chiamato al callback onError () , usando Kotlin ho inventato qualcosa di simile questo in 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))
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.