iOS token過期請求重試方案
業(yè)務(wù)方規(guī)定涌攻,responecode 為1401時需要刷新token山橄,請求重試墙歪,responecode 為202時听系,退出重登。
感謝 AFHTTPSessionManager+RetryPolicy 作者提供方案虹菲,在此基礎(chǔ)上定制自己的方案靠胜。
重試核心代碼: 在請求中添加重試block,傳入success bolck毕源,此處跟原作者不同浪漠,因為我們業(yè)務(wù)方規(guī)定的1401和202是業(yè)務(wù)狀態(tài)碼,不是請求狀態(tài)碼霎褐。所以是要在success中判斷址愿。
NSURLSessionDataTask *task = [self requestUrlWithRetryRemaining:retryCount maxRetry:retryCount retryInterval:retryInterval progressive:progressive fatalStatusCodes:fatalStatusCodes originalRequestCreator:^NSURLSessionDataTask *(void (^retryBlock)(NSURLSessionDataTask *, id)) {
//重試block
//此處可重新設(shè)置head
/* 省略head和簽名的代碼 */
return [self GET:URLString parameters:parameters progress:downloadProgress success:retryBlock failure:failure];
} originalSuccess:success];
return task;
下面看下重試回調(diào)中做了什么。
- (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 觸發(fā)重試冻璃,不走success
2. 并發(fā)時响谓,只走一遍刷新接口
*/
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."];
}
}
細節(jié)部分:
- 如果1401 觸發(fā)重試俱饿。
- 并發(fā)時歌粥,只走一遍刷新token接口。
首先定義一個全局變量RETRY_SEMAPHORE 默認為1拍埠,
給刷新token這塊的代碼加鎖失驶,防止并發(fā)時,無意義的訪問多次刷新token接口枣购,第一個1401進來的時候嬉探,RETRY_SEMAPHORE為1,調(diào)用刷新token接口棉圈,其他1401阻塞涩堤,如果刷新token成功,RETRY_SEMAPHORE置為0分瘾,其他接口進來時胎围,就走else if邏輯,直接重試就好。
如果此時返回202.則RETRY_SEMAPHORE置為-1白魂,所有的1401重重都不走了汽纤,并在同時取消所有的網(wǎng)絡(luò)請求。
if (model.status.integerValue == 202) {
[QZHNetWorkRequest cancleAllRequest];
}
如果是1401福荸,就不走請求完成的回調(diào)了蕴坪。
if (model.status.integerValue != 1401) {
if (completeResult) {
completeResult(model);
}
}