【问题标题】:Retrofit: How do I retry the request again from okhttp interceptor?改造:如何从 okhttp 拦截器再次重试请求?
【发布时间】:2021-11-02 21:07:08
【问题描述】:

我创建了一个拦截器。在某些情况下,我想重试请求“n”次,我该怎么做?

class NetworkErrorHandler constructor():  Interceptor {

    //Central error handling block for errors which has impact all over the app
    override fun intercept(chain: Interceptor.Chain): Response {
        val request =  chain.request()
        var response = chain.proceed(request) 

        return  
            when (response.code) {
                401 -> { 
                    response
                }
                200 ->{  
                    response
                }
                else -> { 
                        var tryCount = 0
                        while (tryCount < 3) {
                            try { 
                                response = chain.proceed(request)
                                tryCount++
                            }catch (e: java.lang.Exception){
                                tryCount++
                            }
                        }  
                    response
                }
            } 
    }
}

它给了我这个错误:

Suppressed: java.lang.IllegalStateException: network interceptor must call proceed() exactly once

如果是,我必须在这里做吗,然后怎么做?

【问题讨论】:

标签: android retrofit retrofit2 okhttp interceptor


【解决方案1】:

我认为您从另一个问题 How to retry HTTP requests with OkHttp/Retrofit? 引导自己(如果没有,我建议您这样做)。您的代码的问题是您的 while 仅受 trycount 限制,而不受响应结果的限制。尝试在那里添加关于响应状态的验证,这样它就不会并行执行 .proceed 方法两次。

// try the request
Response response = chain.proceed(request);

int tryCount = 0;
while (!response.isSuccessful() && tryCount < 3) {

【讨论】:

  • 我能够在这一行的帮助下再次发出请求:response = chain.call().clone().execute()
  • 受tryCount以外的response.code的状态码限制。
  • 第一条评论的解决方案是可能的,但你最终会调用 3 次服务,如果这对你来说没问题,那就完美了。
  • 对于限制,我的意思是在 while 本身,而不是在第一次调用和 while 内的调用之间,我不确定,但我认为编译器会看到有没有限制再次执行while,而如果您放置响应,编译器知道它必须等待调用完成才能继续下一次迭代
  • 我明白你的意思,删除 while 并使用 if 不是更好吗?请在下面查看我的答案。
【解决方案2】:

所以我可以通过使用这条线从拦截器进行另一个调用

response.close()      
chain.call().clone().execute()
                

根据问题的完整代码:

//Central error handling block for errors which has impact all over the app
class NetworkErrorHandler constructor(): Interceptor {

        var tryCount = 0
        override fun intercept(chain: Interceptor.Chain): Response {
            val request = chain.request()
            var response = chain.proceed(request)

            return
            when(response.code) {
                401 - > {
                    response
                }
                200 - > {
                    response
                }
                else - > {

                    if (tryCount < 3) {
                        Thread.sleep(2000)
                        tryCount++
                        response.close()
                        chain.call().clone().execute()

                    }
                    response.newBuilder()
                    .code(401) // Whatever code
                    .body("".toResponseBody(null)) // Whatever body
                    .protocol(Protocol.HTTP_2)
                    .message("Network Error")
                    .request(chain.request())
                    .build()
                }
            }
        } 

【讨论】:

    【解决方案3】:

    这就是我的处理方式:

    @Slf4j
    public class HttpRetryInterceptor implements Interceptor {
        @Override
        public @NotNull Response intercept(Chain chain) throws IOException {
            log.error("Making request for the first time.");
            var request = chain.request();
            Response response = null;
            boolean responseOK = false;
            byte tryCount = 0;
            while (!responseOK && tryCount < 3) {
                try {
                    Thread.sleep(5000 * tryCount);
                    response = chain.proceed(request);
                    log.info("Response is: {}",response);
                    log.info("Response message: {}",response.message());
                    responseOK = response.isSuccessful();
    
                }catch (Exception e){
                    e.printStackTrace();
                    log.error("Request was not successful: {} . Retrying." , tryCount);
                }finally{
                    assert response != null;
                    response.close();
                    tryCount++;
                }
            }
    
            return response != null ? response : chain.proceed(request);
        }
    }
    

    然后我将拦截器添加到我的客户端,例如:new OkHttpClient.Builder().addInterceptor(new HttpRetryInterceptor()).build()

    【讨论】:

      【解决方案4】:

      在Failsafe's OkHttp 支持和RetryPolicy 的支持下,这非常容易:

      RetryPolicy<Response> retryPolicy = RetryPolicy.<Response>builder()
        .handleResultIf(r -> r.code() != 401 && r.code() != 200)
        .withMaxAttempts(3)
        .build();
      Call call = client.newCall(request);
      FailsafeCall failsafeCall = FailsafeCall.with(retryPolicy).compose(call);
      
      // Execute with retries
      Response response = failsafeCall.execute();
      

      【讨论】:

        猜你喜欢
        • 2019-05-27
        • 2019-03-03
        • 2016-04-19
        • 2014-03-27
        • 1970-01-01
        • 2015-12-16
        • 2015-04-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多