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

问题描述 投票:0回答:1

在我的应用程序中,对我的服务器的 2-4 个 API 调用可以在我的 API 类的

NSURLSession
中同时(异步)发生。为了向我的服务器发出 API 请求,我必须在每个
HTTPHeaderField
NSURLRequest
中提供身份验证令牌。 token有效期为一天,如果一天后失效,我需要刷新token。

我在 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
回调。这会导致问题吗?

ios objective-c authentication nsurlsession nsurlrequest
1个回答
-1
投票

不要使用 [self sendTask:],而是尝试使用 [weakSelf 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];
}
© www.soinside.com 2019 - 2024. All rights reserved.