在我的应用中,我的服务器的2-4个API调用可以在我的API类NSURLSession
内同时(异步)进行。为了向我的服务器发出API请求,我必须在每个HTTPHeaderField
的{{1}}中提供身份验证令牌。该令牌有效期为一天,如果它在一天后失效,我需要刷新令牌。
我在API类的以下代码中执行此操作:
NSURLRequest
这个/*!
* @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];
}
方法会在我在应用程序中发出的每个API请求中执行,所以我才意识到这是一个糟糕的方法。如果由于令牌无效(一天过去)而导致3个API请求失败,则所有这3个API请求都将尝试进行API调用以刷新身份验证令牌。
如果有一个API请求失败,有没有办法让我只刷新身份验证令牌,然后重新尝试失败的API调用?
修改
我编辑了问题的标题,表明我正在使用NSURLSession
进步
到目前为止,为了防止多个失败的API请求同时尝试刷新身份验证令牌,我对所有失败的请求都有一个sendTask
,并且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
回调。这会导致问题吗?
答案 0 :(得分:-1)
不要使用[self sendTask:],而是尝试使用[weakSelf sendTask]。检查以下代码:
SELECT DATE_FORMAT(TIMESTAMP, '%b %Y') AS `month`,
COUNT(CASE UserType WHEN 'Administrator' THEN 1 END) AS Administrator,
COUNT(CASE UserType WHEN 'No Role' THEN 1 END) AS NoRole,
COUNT(CASE UserType WHEN 'Super User' THEN 1 END) AS SuperUser,
COUNT(CASE UserType WHEN 'User' THEN 1 END) AS `User`
FROM `user`
GROUP BY DATE_FORMAT(TIMESTAMP, '%b %Y')