Forcing request retry after custom API exceptions

2019-09-05 00:42发布

问题:

I'm really new in RxJava, but I'm trying to implement an API using Retrofit framework and RxJava. On a server side there is an authorization service which handles user's session and in case of some delay in user's actions server breaks his session. After that user has to login again in order to perform new API call. Bad thing is - server always returns HTTP code 200 and for notification about expiration uses some custom JSON response with expiration code, so RxJava doesn't fire Exception during onNext operation because RxJava considers that request was passed successfully.

And the question is: How to implement correct flow to handle custom API exceptions like expiration and retry failed request after some other request (in my case relogin)?

Something like this:

  • app -> login()
  • server -> { code:0, ... }
  • app -> getUsers()
  • server -> { code:0, ... }
  • ------- in 30 minutes -------
  • app -> getPicture()
  • server -> { code:99, ... } // session expired, user unauthorized
  • app -> login()
  • server -> { code:0, ... }
  • app -> getPicture()
  • server -> { code:0, ... }

I was thinking about something like this, but with no success:

        Observable.create(new Observable.OnSubscribe<BackendResponse<String>>() {
        @Override
        public void call(Subscriber<? super Response<String>> subscriber) {
            try {
                Response<String> response;
                subscriber.onNext(response = getInterface().getUsers());

                if (response != null) {
                  response.checkData(); // throws ServerException in case of code != 0
                }
                subscriber.onCompleted();
            } catch (Exception e) {
                subscriber.onError(e);
            }
        }
    }).subscribeOn(Schedulers.io()).retryWhen(new RetryWithSessionRefresh(new SessionService())).subscribe();

and RetryWithSessionRefresh is:

public class RetryWithSessionRefresh implements
    Func1<Observable<? extends Notification<?>>, Observable<?>> {

private final SessionService sessionSerivce;

public RetryWithSessionRefresh(SessionService sessionSerivce) {
    this.sessionSerivce = sessionSerivce;
}

@Override
public Observable<?> call(Observable<? extends Notification<?>> attempts) {
    return attempts
            .flatMap(new Func1<Notification<?>, Observable<?>>() {
                @Override
                public Observable<?> call(final Notification notification) {
                    final Throwable throwable = notification.getThrowable();
                    if (throwable instanceof ServerException) {
                        final ServerException backendException = (ServerException) throwable;
                        if (backendException.getBackendErrorCode() == Response.AUTHORIZATION_FAILED) {
                            return sessionSerivce
                                    .observeSessionToken()
                                    .doOnNext(new Action1<TokenCallback>() {
                                        @Override
                                        public void call(TokenCallback token) {
                                            if (token != null) {
                                                DataHolder.getInstance().setAuthToken(token.getToken());
                                            }
                                        }
                                    })
                                    .doOnError(new Action1<Throwable>() {
                                        @Override
                                        public void call(Throwable throwable) {
                                            DataHolder.getInstance().setAuthToken("");
                                        }
                                    });
                        }
                    }
                    return Observable.error(notification.getThrowable());
                }
            });
}

回答1:

Maybe you van flapMap your response, and return your observable in case of success with Observable.just(your item) or an error when the response isn't valid with Observable.error(your error)