改造:如何从 okhttp 拦截器再次重试请求?
Posted
技术标签:
【中文标题】改造:如何从 okhttp 拦截器再次重试请求?【英文标题】:Retrofit: How do I retry the request again from okhttp interceptor? 【发布时间】: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
如果是,我必须在这里做吗,然后怎么做?
【问题讨论】:
我想我们不能:square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/… 【参考方案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();
【讨论】:
以上是关于改造:如何从 okhttp 拦截器再次重试请求?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 OkHttp/Retrofit 重试 HTTP 请求?