iOS-重新尝试NSURLSession中的NSURLRequests失败

iOS-重新尝试NSURLSession中的NSURLRequests失败,ios,api,authentication,nsurlsession,nsurlrequest,Ios,Api,Authentication,Nsurlsession,Nsurlrequest,在我的应用程序中,对我的服务器的2-4个API调用可以在我的API类的NSURLSession中同时(异步)发生。为了向我的服务器发出API请求,我必须在每个NSURLRequest的HTTPHeaderField中提供身份验证令牌。令牌有效期为一天,如果一天之后无效,我需要刷新令牌 我在API类中的以下代码中执行此操作: /*! * @brief sends a request as an NSHTTPURLResponse. This method is private. * @para

在我的应用程序中,对我的服务器的2-4个API调用可以在我的API类的
NSURLSession
中同时(异步)发生。为了向我的服务器发出API请求,我必须在每个
NSURLRequest
HTTPHeaderField
中提供身份验证令牌。令牌有效期为一天,如果一天之后无效,我需要刷新令牌

我在API类中的以下代码中执行此操作:

/*!
 * @brief sends a request as an NSHTTPURLResponse. This method is private.
 * @param request The request to send.
 * @param success A block to be called if the request is successful.
 * @param error A block to be called if the request fails.
 */
-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback
{
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
    {
        [self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error)
        {
            //if auth token expired and getting "not authenticated" error (status 401)
            NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
            if (httpResp.statusCode == 401) {
                [self refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) {
                    self.authToken = response[@"token"];
                    //attempt to re-try the request that failed due to token expiration
                    [self sendTask:request successCallback:success errorCallback:errorCallback];
                } errorCallback:^(NSString *error) {
                    //two weeks have passed and the token is no longer refreshable
                    NSLog(@"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY");
                }];
            }
        }];
    }];
    [task resume];
}
这个
sendTask
方法会在我在应用程序中发出的每个API请求中执行,所以我才意识到这是一种不好的方法。如果3个API请求由于令牌无效而失败(一天过去了),那么所有3个API请求都将尝试进行API调用以刷新身份验证令牌

如果其中一个API请求失败,是否有办法只刷新一次身份验证令牌,然后重新尝试失败的API调用

编辑

我编辑了问题的标题,以表明我正在使用NSURLSession

进步

到目前为止,为了防止多个失败的API请求同时尝试刷新身份验证令牌,我为所有失败的请求提供了一个
NSArray
,还有一个
NSNumber
,用作锁,以确保身份验证令牌只尝试刷新一次。我使用以下代码执行此操作:

-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback
{
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
    {
        MyAPIInterface *__weak weakSelf = self;
        [self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error)
        {
            NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
            if (httpResp.statusCode == 401) {
                if ([error isEqualToString:@"invalid_credentials"]) {
                    errorCallback(@"Invalid username and/or password");
                }
                else if ([error isEqualToString:@"Unknown error"]) {
                    errorCallback(error);
                }
                else {
                    if (!weakSelf.alreadyRefreshingToken.boolValue) {

                        //lock alreadyRefreshingToken boolean
                        weakSelf.alreadyRefreshingToken = [NSNumber numberWithBool:YES];
                        NSLog(@"NOT REFRESHING TOKEN");

                        // add failed request to failedRequests array
                        NSMutableArray *mutableFailedRequests = [weakSelf.failedRequests mutableCopy];
                        [mutableFailedRequests addObject:request];
                        weakSelf.failedRequests = [mutableFailedRequests copy];

                        // refresh auth token
                        [weakSelf refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) {

                            //store authToken
                            weakSelf.authToken = response[@"token"];
                            NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
                            [defaults setObject:weakSelf.authToken forKey:@"authToken"];
                            [defaults synchronize];

                            //attempt to re-try all requests that failed due to token expiration
                            for (NSURLRequest *failedRequest in weakSelf.failedRequests) {
                                [weakSelf sendTask:failedRequest successCallback:success errorCallback:errorCallback];
                            }

                            //clear failedRequests array and unlock alreadyRefreshingToken boolean
                            [weakSelf clearFailedRequests];
                            weakSelf.alreadyRefreshingToken = [NSNumber numberWithBool:NO];

                            NSLog(@"TOKEN REFRESHING SUCCESSFUL THO");

                        } errorCallback:^(NSString *error) {

                            NSLog(@"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY");

                            //clear failedRequests array
                            [weakSelf clearFailedRequests];

                            errorCallback(@"Your login session has expired");

                        }];
                    }
                    else  {
                        NSLog(@"ALREADY REFRESHING TOKEN. JUST ADD TO FAILED LIST");
                        NSMutableArray *mutableFailedRequests = [weakSelf.failedRequests mutableCopy];
                        [mutableFailedRequests addObject:request];
                        weakSelf.failedRequests = [mutableFailedRequests copy];
                    }
                }
            }
            else {
                NSLog(@"ERROR STRING THO: %@", error);
                errorCallback(error);
            }
        }];
    }];
    [task resume];
}

#pragma mark Custom Methods

-(void)clearFailedRequests {
    NSMutableArray *mutableFailedRequests = [self.failedRequests mutableCopy];
    [mutableFailedRequests removeAllObjects];
    self.failedRequests = [mutableFailedRequests copy];
}

我这样做对吗?有一点让我有些偏执,那就是我并没有在某些时候真正调用
success
error
回调。这会导致问题吗?

请尝试使用[weakSelf sendTask],而不是使用[self sendTask:]。检查以下代码:

-(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback
{

   __weak __typeof(self)weakSelf = self;
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
    {
        [self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error)
        {
            //if auth token expired and getting "not authenticated" error (status 401)
            NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
            if (httpResp.statusCode == 401) {
                [self refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) {
                    self.authToken = response[@"token"];
                    //attempt to re-try the request that failed due to token expiration
                    [weakSelf sendTask:request successCallback:success errorCallback:errorCallback];
                } errorCallback:^(NSString *error) {
                    //two weeks have passed and the token is no longer refreshable
                    NSLog(@"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY");
                }];
            }
        }];
    }];
    [task resume];
}

呜呜,谢谢你的接球。我忘了把脆弱的自我放在那里。但这将如何防止多个失败的请求同时尝试刷新令牌?不会,但它只修复了重试问题。