REST API在Retrofit2 RxJava中返回401时刷新访问令牌

时间:2019-07-10 13:21:40

标签: java android retrofit2 rx-java2

我在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 个答案:

答案 0 :(得分:1)

使用翻新的Authenticator API并在此调用访问令牌api内获取访问令牌,然后使用此访问令牌重试失败的API。