美文网首页常用
iOS token过期请求重试方案

iOS token过期请求重试方案

作者: Neal_f | 来源:发表于2019-09-25 21:06 被阅读0次

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

    细节部分:

    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过期请求重试方案

        本文链接:https://www.haomeiwen.com/subject/vzvwuctx.html