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);
}
}