如何有条件地链接几个翻新呼叫?

时间:2019-10-02 12:14:08

标签: java android retrofit

我在http:/some.api有一个api和一个GET端点data,它需要通过通过POST auth端点接收到的承载令牌进行认证。给定的安全令牌每1小时过期一次,因此,如果我收到POST http:/some.api/auth上的401消息,我需要发出一个GET http:/some.api/data请求,续订访问令牌并使用新的令牌对data进行相同的调用在我的客户不了解令牌的情况下访问令牌。当前示例提供了Call类型,我可以在不停止的情况下从UI线程enqueue(调用),类似于.net的{​​{1}}功能。现在,如果我使用的是async/await,那么我将总结整个逻辑,如下所示:

.net

并从UI线程中调用async Task<DataModel> GetDataAsync() { try { return await GetDataInternalAsync(); } catch(InvalidTokenException) { await ReAuthenticateAsync(); return await GetDataInternalAsync(); } } ,无需担心。但是,当前示例仅显示如何进行调用并通过调用方法“现场”处理结果,例如:

await GetDataAsync()

使用翻新来组织满足我需要的呼叫体系结构的方式是什么?

2 个答案:

答案 0 :(得分:2)

我强烈建议您使用RxJava adapter,而不是使用普通的翻新Callbacks。然后,您可以从Retrofit Client返回Observables,这在链接不同的电话(例如,使用flatMap运算符)时将使您的生活更加轻松。

要处理您提到的令牌认证逻辑,我将使用自定义OkHttp3 Authenticator进行。这样,您可以将所有身份验证逻辑(可能将在几乎所有API调用中使用)放在一个位置,并且OkHttp将在每次收到401响应时调用定义的逻辑来更新令牌。

public class CustomOkHttpAuthenticator implements Authenticator {

    ...

    @Override
    public Request authenticate(@NonNull Route route, @NonNull okhttp3.Response response) throws IOException {
        if (response.code() == 401) {
          // Call your refresh token endpoint, wait for the new token, and build
          // again the original request using the new token you received.
        }
        return null;
    }`

    ...
}

最后,在创建OkHttp客户端时,Retrofit将用于执行HTTP调用,请确保添加自定义Authenticator

OkHttpClient.Builder client = new OkHttpClient.Builder();
        client.connectTimeout(10, TimeUnit.SECONDS);
        client.readTimeout(15, TimeUnit.SECONDS);
        client.writeTimeout(15, TimeUnit.SECONDS);
        ...
        client.authenticator(new CustomOkHttpAuthenticator());
        OkHttpClient okHttpClient = client.build();
        okHttpClientHolder.setOkHttpClient(okHttpClient);
        ...

您可以在this repository中查看完整的代码示例。

答案 1 :(得分:0)

尝试一下:

private static final int MAX_TRIES = 3;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getDataAsync(0);
}

private void getDataAsync(final int currentRetryCount) {
    Call<DataModel> call = apiService.getData();
    call.enqueue(new Callback<DataModel>() {
        @Override
        public void onResponse(@NonNull Call<DataModel> call, @NonNull Response<DataModel> response) {
            if(currentRetryCount<MAX_TRIES && response.code()== HttpURLConnection.HTTP_UNAUTHORIZED){ //401
                authRequest(currentRetryCount+1);
            }
        }

        public void onFailure(@NonNull Call<DataModel> call, @NonNull Throwable t) {
            // todo deal with the failed network request
        }
    });
}

private void authRequest(final int currentRetryCount){
    Call<AuthResponse> call = apiService.authReq();
    call.enqueue(new Callback<AuthResponse>() {
        @Override
        public void onResponse(@NonNull Call<AuthResponse> call, @NonNull Response<AuthResponse> response) {
            getDataAsync(currentRetryCount);
        }

        @Override
        public void onFailure(@NonNull Call<AuthResponse> call, @NonNull Throwable t) {
            // todo deal with the failed network request
        }
    });
}

我添加了retryCount来防止无限请求,像这样的cz链接请求也可以充当递归。