Как изменить параметры повторного запроса после ошибки в RxJava

Я отправляю запрос на вход на сервер с модификацией 2.0, и сервер возвращает токен сеанса клиента, который я должен использовать в других запросах, но этот токен имеет ограниченный срок службы, и когда срок его действия истекает, сервер возвращает HTTP-ошибку 401.

Я пытаюсь повторно войти в систему после получения этой ошибки с помощью следующего кода:

    holder.getApi(GuideProfileApi.class)
      .getProfile(String.valueOf(holder.getServerId()), holder.getServerToken())
      .subscribeOn(Schedulers.io())
      .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
         @Override
         public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
           return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
             @Override
             public ObservableSource<?> apply(Throwable throwable) throws Exception {
               if (throwable instanceof HttpException &&  ((HttpException)throwable).code() == 401) {
                 RegistryLoginResult loginResult = holder.login().blockingSingle();
                 return holder.getApi(GuideProfileApi.class)
                    .getProfile(String.valueOf(loginResult.getUserId()), loginResult.getSessionToken());
               }
               return Observable.error(throwable);
            }
        });
     }
  })
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Consumer<ProfileResult>() {
    @Override
    public void accept(ProfileResult profileResult) throws Exception {
      Log.d("Result", profileResult.toString());
    }
  }, new Consumer<Throwable>() {
      @Override
      public void accept(Throwable throwable) throws Exception {
        Log.e("Result", throwable.getLocalizedMessage());
      }
});

И повторный запрос отправляется, но параметры запроса такие же, как и в некорректном запросе (до повторного входа в систему). Как я могу изменить параметры запроса перед его повторной отправкой?


person Pavel    schedule 15.01.2017    source источник


Ответы (2)


Вы используете не того оператора. retryWhen повторит попытку вашего исходного наблюдаемого, если обнаружит ошибку. Вам нужно onErrorResumeNext. Что-то типа

 holder.getApi(GuideProfileApi.class)
      .getProfile(String.valueOf(holder.getServerId()), holder.getServerToken())
      .subscribeOn(Schedulers.io())
      .onErrorResumeNext(new Function<Throwable, ObservableSource<?>>() {
        @Override
         public ObservableSource<?> apply(Throwable throwable) {
               if (throwable instanceof HttpException &&  ((HttpException)throwable).code() == 401) {
                 RegistryLoginResult loginResult = holder.login().blockingSingle();
                 return holder.getApi(GuideProfileApi.class)
                    .getProfile(String.valueOf(loginResult.getUserId()), loginResult.getSessionToken());
               }
               return Observable.error(throwable);
        }
    }) 
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Consumer<ProfileResult>() {
    @Override
    public void accept(ProfileResult profileResult) throws Exception {
      Log.d("Result", profileResult.toString());
    }
  }, new Consumer<Throwable>() {
      @Override
      public void accept(Throwable throwable) throws Exception {
        Log.e("Result", throwable.getLocalizedMessage());
      }
});
person JohnWowUs    schedule 16.01.2017
comment
Как исключить return holder.getApi(GuideProfileApi.class) .getProfile(String.valueOf(loginResult.getUserId()), loginResult.getSessionToken()); и изменить параметры исходного наблюдаемого? - person Pavel; 17.01.2017
comment
Я считаю, что Observables в rxjava неизменяемы. После того, как вы создали наблюдаемую, вы не можете вернуться и изменить ее. - person JohnWowUs; 17.01.2017
comment
Спасибо за консультацию. - person Pavel; 17.01.2017
comment
Но что, если второй наблюдаемый обнаружит ошибку, и вы захотите повторить попытку еще раз? Похоже, что решение отсрочки, предложенное Полом, намного чище. - person Scott Cooper; 23.02.2017

Вы можете использовать retryWhen, но проблема в том, что ваш retryWhen повторяет тот же наблюдаемый объект, который вы создаете в ленивый момент. Ваше решение здесь — использовать оператор defer для получения host(), так как defer не создает наблюдаемое, когда вы его определяете, а когда оно потребляется подписчиком.

Observable.defer(()-> holder.getApi(GuideProfileApi.class)
          .getProfile(String.valueOf(holder.getServerId()),holder.getServerToken()))
  .subscribeOn(Schedulers.io())
  .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
     @Override
     public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
       return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
         @Override
         public ObservableSource<?> apply(Throwable throwable) throws Exception {
           if (throwable instanceof HttpException &&  ((HttpException)throwable).code() == 401) {
             RegistryLoginResult loginResult = holder.login().blockingSingle();
             return holder.getApi(GuideProfileApi.class)
                .getProfile(String.valueOf(loginResult.getUserId()), loginResult.getSessionToken());
           }
           return Observable.error(throwable);
        }
    });
 }
  })
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Consumer<ProfileResult>() {
    @Override
    public void accept(ProfileResult profileResult) throws Exception {
      Log.d("Result", profileResult.toString());
    }
      }, new Consumer<Throwable>() {
          @Override
          public void accept(Throwable throwable) throws Exception {
            Log.e("Result", throwable.getLocalizedMessage());
          }
    });

Вы можете увидеть несколько примеров повторных попыток здесь https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/errors/ObservableExceptions.java

person paul    schedule 16.01.2017