Обновление токена доступа, когда REST API возвращает 401 в Retrofit2 RxJava - PullRequest
0 голосов
/ 10 июля 2019

У меня есть следующие конечные точки в REST API:


public interface AutomoticzAPI {

    @POST("/api/beacon_auth/login")
    Single<LoginResponse> login(@Body LoginRequest request);

    @GET("/api/system/ws_devices")
    Single<WSDevicesResponse> wsDeviceList(@Header("Authorization") String tokenHeader);

}

Когда я вызываю login конечную точку, в ответ я получаю токен доступа, который я сохраняю в ClientSession объект-держатель. Позже я смогу извлечь токен из ClientSession для вызова защищенных ресурсов сервера:

        api.login(ClientSession.getInstance().getLoginRequest(login, password))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(loginResponse -> {
                    String accessToken = loginResponse.getAccessToken();
                    ClientSession.getInstance().setAccessToken(accessToken);
                    view.onLoginSuccess();
                }, throwable -> {
                    RetrofitException exception = (RetrofitException) throwable;
                    if (exception.getKind().equals(RetrofitException.Kind.HTTP)){
                        view.onLoginFailed(exception.getMessage());
                    } else if(exception.getKind().equals(RetrofitException.Kind.NETWORK))
                    {
                        view.onLoginFailed("Network error...");
                    } else {
                        view.onLoginFailed("Unknown error occurred...");
                    }
                });

Когда я звоню на конечную точку wsDeviceList, сервер может вернуть код ответа 401 HTTP и тело json с кодом ошибки и сообщением:

{
    "code": "EXPIRED-TOKEN",
    "message": "Token expired"
}

Если это произойдет, я хочу еще раз позвонить в конечную точку входа в систему, чтобы получить новый токен доступа. Вот мой код:

    ClientSession clientSession = ClientSession.getInstance();
    String token = "Bearer "+clientSession.getAccessToken();
    String url = ClientSession.getInstance().getUrl();
    AutomoticzAPI api = NetworkManager.getApiClient(url);
    api.wsDeviceList(token)
            .retryWhen(throwableFlowable -> throwableFlowable.flatMap(
                    new Function<Throwable, Publisher<?>>() {
                        @Override
                        public Publisher<?> apply(Throwable throwable) throws Exception {
                            RetrofitException exception = (RetrofitException) throwable;
                            if (exception.isUnauthorizedError()){
                                return relogin(api, clientSession.getLoginRequest());
                            }
                            return (Publisher<?>) throwable;
                        }
                    }
            ))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(wsDevicesResponse -> {
                view.onDeviceListLoaded(wsDevicesResponse.getWsdevices());
            }, throwable -> {
                RetrofitException exception = (RetrofitException) throwable;
                view.onError(exception);
            });
}

public Publisher<?> relogin(AutomoticzAPI api, LoginRequest loginRequest){
    return (Publisher<?>) api.login(loginRequest)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(loginResponse -> {
                String accessToken = loginResponse.getAccessToken();
                ClientSession.getInstance().setAccessToken(accessToken);
            }, throwable -> {
                RetrofitException exception = (RetrofitException) throwable;
                view.onError(exception);
            });
}

Но когда запускается метод relogin, моя программа падает. Я не опытный в RxJava и, вероятно, делаю это неправильно. Как я могу вспомнить login, чтобы обновить токен доступа, а затем снова вызвать wsDeviceList?

1 Ответ

1 голос
/ 10 июля 2019

Используйте Authenticator API для модификации и внутри этого api токена доступа к вызову, чтобы получить токен доступа, и повторите попытку вызова API с ошибкой, используя этот токен доступа.

...