iOS token过期请求重试方案

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 *)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 *)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."];
    }
}

细节部分:

  1. 如果1401 触发重试。
  2. 并发时,只走一遍刷新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);
        }
    }

你可能感兴趣的:(iOS token过期请求重试方案)