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.