Отримайте код статусу відповіді за допомогою Retrofit 2.0 та RxJava


107

Я намагаюся оновити до Retrofit 2.0 і додати RxJava в проект Android. Я роблю api-дзвінок і хочу отримати код помилки у разі відповіді на помилку з сервера.

Observable<MyResponseObject> apiCall(@Body body);

І в дзвінку 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
        }
    });

У Retrofit 1.9 RetrofitError все ще існував, і ми могли отримати статус, виконавши:

error.getResponse().getStatus()

Як це зробити з Retrofit 2.0 за допомогою RxJava?

Відповіді:


185

Замість того, щоб оголошувати виклик API, як ви зробили:

Observable<MyResponseObject> apiCall(@Body body);

Ви також можете оголосити це так:

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

Потім у вас буде абонент, як:

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

Отже, відповіді сервера з кодом помилки також будуть доставлені на onNextвас, і ви можете отримати код, зателефонувавши reponse.code().

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

РЕДАКТ: Добре, я нарешті розібрався з тим, що сказав е-нурі у своєму коментарі, а саме, що лише 2xx коди будуть робити onNext. Виявляється, ми обидва праві:

Якщо виклик оголошено так:

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

або навіть це

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

всі відповіді потраплять onNext, незалежно від коду помилки. Це можливо, тому що все загорнуте в Responseоб’єкт Retrofit.

Якщо, з іншого боку, дзвінок оголошується так:

Observable<MyResponseObject> apiCall(@Body body);

або це

Observable<ResponseBody> apiCall(@Body body);

насправді будуть надходити лише 2xx відповіді onNext. Все інше буде загорнуто у HttpExceptionта відправлено до onError. Що також має сенс, адже без Responseобгортки, що слід випромінювати onNext? Зважаючи на те, що запит не був успішним, єдиною розумною справою було б null...


16
Для людей, які шукають HTTP-коди 4xx, вони виявляться як HttpException в onError. onNext матиме лише 2xx.
e-nouri

2
Це цікаво ... Я лише перевірив ще раз ( gist.github.com/DavidMihola/17a6ea373b9312fb723b ) і всі коди, які я намагався виявити,onNext включаючи 404, і т. Д. Я використовував Retrofit v2.0.0-beta3.
david.mihola

@ e-nouri: Я щойно додав абзац до своєї відповіді, який враховує ваш коментар!
david.mihola

1
@ david.mihola Якщо створити модернізацію через add GsonConverterFactory, так само Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()), як отримати оригінальну відповідь?
Honghe.Wu

1
Крім поводження з помилками, я думаю, що ви, можливо, захочете перевірити будь-які заголовки відповідей, що поставляються разом із тілом, - це також можливо лише у разі отримання Response. Я також його рідко використовую - але думаю, що добре знати, що він існує.
david.mihola

112

Всередині методу onError поставте це, щоб отримати код

((HttpException) e).code()

7
Це може бути однією з найбільш занижених відповідей, які я коли-небудь бачив у ЗП. Це геній. Ви можете зробити все, що може знадобитися для відповіді HttpException. Я використовую RxJava, і мені потрібно було проаналізувати відповідь після отримання HTTP 400 BAD REQUEST. Велике спасибі!
ГабріельОширо

29
Просто переконайтеся, що це раніше екземпляр HttpException, оскільки NetworkErrors буде IOExceptions і не має коду статусу.
Бенджамін Месінг

Щоб продовжити від @BenjaminMesing сказав про NetworkError, якщо ви більше операторів нижче за течією (map / flatMap / forEach і т. Д.) У вашому Rx перед тим, як підписатись, то може виникнути безліч можливих типів винятків, а не обов’язково помилка на мережевий запит.
Марк Кін

java.lang.ClassCastException: java.lang.Throwable не може бути передано до модернізації2.HttpException
Хтось десь

7

Ви повинні зауважити, що станом на Retrofit2 всі відповіді з кодом 2xx будуть викликані з зворотного виклику onNext (), а решта HTTP-кодів, таких як 4xx, 5xx буде викликано при зворотному звороті onError () , використовуючи Kotlin, я придумав щось подібне це в 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))
  }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.