iOS token过期请求重试方案
业务方规定,responecode 为1401时需要刷新token,请求重试,responecode 为202时,退出重登。
感谢 AFHTTPSessionManager+RetryPolicy 作者提供方案,在此基础上定制自己的方案。
重试核心代码: 在请求中添加重试block,传入success bolck,此处跟原作者不同,因为我们业务方规定的1401和202是业务状态码,不是请求状态码。所以是要在success中判断。
NSURLSessionDataTask *task = [self requestUrlWithRetryRemaining:retryCount maxRetry:retryCount retryInterval:retryInterval progressive:progressive fatalStatusCodes:fatalStatusCodes originalRequestCreator:^NSURLSessionDataTask *(void (^retryBlock)(NSURLSessionDataTask *, id)) {
//重试block
//此处可重新设置head
/* 省略head和签名的代码 */
return [self GET:URLString parameters:parameters progress:downloadProgress success:retryBlock failure:failure];
} originalSuccess:success];
return task;
下面看下重试回调中做了什么。
- (NSURLSessionDataTask *)requestUrlWithRetryRemaining:(NSInteger)retryRemaining maxRetry:(NSInteger)maxRetry retryInterval:(NSTimeInterval)retryInterval progressive:(bool)progressive fatalStatusCodes:(NSArray<NSNumber *> *)fatalStatusCodes originalRequestCreator:(NSURLSessionDataTask *(^)(void (^)(NSURLSessionDataTask *, id)))taskCreator originalSuccess:(void(^)(NSURLSessionDataTask *task, id ))success {
QZHWS(weakSelf);
void(^retryBlock)(NSURLSessionDataTask *,id) = ^(NSURLSessionDataTask *task,id responseObject) {
QZHRespModel *model = [QZHRespModel yy_modelWithJSON:responseObject];
/*
1. 如果1401 触发重试,不走success
2. 并发时,只走一遍刷新接口
*/
NSNumber *fatalStatusCode = fatalStatusCodes.firstObject;
if (model.status.integerValue == fatalStatusCode.integerValue) {
//1401,重试
@synchronized (self) {
if (RETRY_SEMAPHORE == 1) {
[Credigo_UserVM fetchAccessToken:^{
RETRY_SEMAPHORE = 0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
RETRY_SEMAPHORE = 1;
});
[weakSelf retryRemaining:retryRemaining maxRetry:maxRetry retryInterval:retryInterval progressive:progressive fatalStatusCodes:fatalStatusCodes originalRequestCreator:taskCreator originalSuccess:success];
}];
} else if (RETRY_SEMAPHORE == 0){
[weakSelf retryRemaining:retryRemaining maxRetry:maxRetry retryInterval:retryInterval progressive:progressive fatalStatusCodes:fatalStatusCodes originalRequestCreator:taskCreator originalSuccess:success];
}
}
} else {
if (model.status.integerValue == 202) {
RETRY_SEMAPHORE = -1;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
RETRY_SEMAPHORE = 1;
});
}
}
success(task,responseObject);
};
NSURLSessionDataTask *task = taskCreator(retryBlock);
return task;
}
- (void)retryRemaining:(NSInteger)retryRemaining maxRetry:(NSInteger)maxRetry retryInterval:(NSTimeInterval)retryInterval progressive:(bool)progressive fatalStatusCodes:(NSArray<NSNumber *> *)fatalStatusCodes originalRequestCreator:(NSURLSessionDataTask *(^)(void (^)(NSURLSessionDataTask *, id)))taskCreator originalSuccess:(void(^)(NSURLSessionDataTask *task, id ))success {
if (retryRemaining > 0) {
void (^addRetryOperation)(void) = ^{
[self requestUrlWithRetryRemaining:retryRemaining - 1 maxRetry:maxRetry retryInterval:retryInterval progressive:progressive fatalStatusCodes:fatalStatusCodes originalRequestCreator:taskCreator originalSuccess:success];
};
if (retryInterval > 0.0) {
dispatch_time_t delay;
if (progressive) {
delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * pow(2, maxRetry - retryRemaining) * NSEC_PER_SEC));
[self logMessage:@"Delaying the next attempt by %.0f seconds 登录", retryInterval * pow(2, maxRetry - retryRemaining)];
} else {
delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC));
[self logMessage:@"Delaying the next attempt by %.0f seconds 登录", retryInterval];
}
// Not accurate because of "Timer Coalescing and App Nap" - which helps to reduce power consumption.
dispatch_after(delay, dispatch_get_main_queue(), ^(void){
addRetryOperation();
});
} else {
[self logMessage:@"Delaying the next attempt by %.0f seconds 登录", retryInterval];
}
} else {
[self logMessage:@"No more attempts left! Will execute the failure block."];
}
}
细节部分:
- 如果1401 触发重试。
- 并发时,只走一遍刷新token接口。
首先定义一个全局变量RETRY_SEMAPHORE 默认为1,
给刷新token这块的代码加锁,防止并发时,无意义的访问多次刷新token接口,第一个1401进来的时候,RETRY_SEMAPHORE为1,调用刷新token接口,其他1401阻塞,如果刷新token成功,RETRY_SEMAPHORE置为0,其他接口进来时,就走else if逻辑,直接重试就好。
如果此时返回202.则RETRY_SEMAPHORE置为-1,所有的1401重重都不走了,并在同时取消所有的网络请求。
if (model.status.integerValue == 202) {
[QZHNetWorkRequest cancleAllRequest];
}
如果是1401,就不走请求完成的回调了。
if (model.status.integerValue != 1401) {
if (completeResult) {
completeResult(model);
}
}