Obtenez le code d'état de la réponse à l'aide de Retrofit 2.0 et RxJava

108

J'essaye de passer à Retrofit 2.0 et d'ajouter RxJava dans mon projet Android. Je fais un appel à l'API et je souhaite récupérer le code d'erreur en cas de réponse d'erreur du serveur.

Observable<MyResponseObject> apiCall(@Body body);

Et dans l'appel 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
        }
    });

Dans Retrofit 1.9, RetrofitError existait toujours et nous pouvions obtenir le statut en faisant:

error.getResponse().getStatus()

Comment faites-vous cela avec Retrofit 2.0 en utilisant RxJava?

UN B
la source

Réponses:

185

Au lieu de déclarer l'appel API comme vous l'avez fait:

Observable<MyResponseObject> apiCall(@Body body);

Vous pouvez également le déclarer comme ceci:

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

Vous aurez alors un abonné comme le suivant:

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

Ainsi, les réponses du serveur avec un code d'erreur seront également envoyées à onNextet vous pouvez obtenir le code en appelant reponse.code().

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

EDIT: OK, j'ai finalement commencé à regarder ce que e-nouri disait dans leur commentaire, à savoir que seuls les codes 2xx le feront onNext. Il s'avère que nous avons tous les deux raison:

Si l'appel est déclaré comme ceci:

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

ou même ça

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

toutes les réponses aboutiront onNext, quel que soit leur code d'erreur. Ceci est possible car tout est enveloppé dans un Responseobjet par Retrofit.

Si, au contraire, l'appel est déclaré comme ceci:

Observable<MyResponseObject> apiCall(@Body body);

ou ca

Observable<ResponseBody> apiCall(@Body body);

en effet, seules les réponses 2xx iront à onNext. Tout le reste sera emballé dans un HttpExceptionfichier et envoyé à onError. Ce qui a également du sens, car sans le Responsewrapper, à quoi doit- on émettre onNext? Étant donné que la demande n'a pas abouti, la seule chose sensée à émettre serait null...

david.mihola
la source
16
Pour les personnes recherchant les codes HTTP 4xx, ils se retrouveront sous le nom HttpException dans onError. le onNext n'aura que le 2xx.
e-nouri
2
C'est intéressant ... Je viens de vérifier à nouveau ( gist.github.com/DavidMihola/17a6ea373b9312fb723b ) et tous les codes que j'ai essayés finissent par onNextinclure 404, etc. J'ai utilisé Retrofit v2.0.0-beta3.
david.mihola
@ e-nouri: Je viens d'ajouter un paragraphe à ma réponse qui tient compte de votre commentaire!
david.mihola
1
@ david.mihola Si Build Retrofit via add GsonConverterFactory, tout comme Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()), Comment obtenir la réponse d'origine?
Honghe.Wu
1
Outre la gestion des erreurs, je pense que vous voudrez peut-être être en mesure d'inspecter tous les en-têtes de réponse fournis avec le corps - ce n'est également possible que si vous obtenez le fichier Response. Je l'utilise rarement aussi - mais je pense qu'il est bon de savoir qu'il existe.
david.mihola
112

Dans la méthode onError, mettez ceci pour obtenir le code

((HttpException) e).code()
Diveno
la source
7
C'est peut-être l'une des réponses les plus sous-estimées que j'aie jamais vues dans SO. C'est du génie. Vous pouvez faire tout ce dont vous pourriez avoir besoin avec la réponse HttpException. J'utilise RxJava et j'avais besoin d'analyser la réponse après avoir reçu un fichier HTTP 400 BAD REQUEST. Merci beaucoup!
GabrielOshiro
29
Assurez-vous simplement de vérifier s'il s'agit d'une instance de HttpException avant car NetworkErrors sera IOExceptions et n'a pas de code d'état.
Benjamin Mesing
Pour faire suite à @BenjaminMesing dit à propos de NetworkError, si vous faites plus d'opérateurs en aval (map / flatMap / forEach etc.) dans votre Rx avant de vous abonner, il pourrait y avoir une multitude de types d'exceptions possibles, et pas nécessairement une erreur sur la demande réseau.
Mark Keen
java.lang.ClassCastException: java.lang.Throwable ne peut pas être converti en retrofit2.HttpException
Someone Somewhere
7

Vous devez noter qu'à partir de Retrofit2, toutes les réponses avec le code 2xx seront appelées à partir du rappel onNext () et le reste des codes HTTP comme 4xx, 5xx sera appelé sur le rappel onError () , en utilisant Kotlin j'ai trouvé quelque chose comme ceci dans le 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))
  }
MohammadReza
la source