23

私は最近、ASIHTTPRequest から AFNetworking に移行しました。ただし、接続しているサーバーにはいくつかの問題があり、リクエストがタイムアウトすることがあります。ASIHTTPRequest を使用すると、次のセレクターを使用して、タイムアウトが発生した場合にリクエストの再試行回数を設定できました。

-setNumberOfTimesToRetryOnTimeout:

これは、この投稿でさらに参照できます。ASIHTTPRequest を再試行できますか?

慣れていない場合は、これは AFNetworking です https://github.com/AFNetworking/AFNetworking#readme

AFNetworking で同等の API を見つけることができませんでした。AFNetworking を使用してタイムアウトが発生した場合にネットワーク要求を再試行するための解決策を見つけた人はいますか?

4

4 に答える 4

59

AFNetworkingの開発者であるMattThompsonは、親切にもこれに答えてくれました。以下は、解決策を説明するgithubリンクです。

https://github.com/AFNetworking/AFNetworking/issues/393

基本的に、AFNetworkingはこの機能をサポートしていません。以下に示すように、ケースバイケースで実装するのは開発者に任されています(githubでのMatt Thompsonの回答から引用)

- (void)downloadFileRetryingNumberOfTimes:(NSUInteger)ntimes 
                              success:(void (^)(id responseObject))success 
                              failure:(void (^)(NSError *error))failure
{
    if (ntimes <= 0) {
        if (failure) {
            NSError *error = ...;
            failure(error);
        }
    } else {
        [self getPath:@"/path/to/file" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
            if (success) {
                success(...);
            }
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            [self downloadFileRetryingNumberOfTimes:ntimes - 1 success:success failure:failure];
        }];
    }
}
于 2012-09-06T23:26:49.540 に答える
2

ApiClient クラスにプライベート メソッドを実装しました。

- (void)sendRequest:(NSURLRequest *)request successBlock:(void (^)(AFHTTPRequestOperation *operation, id responseObject))successBlock failureBlock:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failureBlock
{
    __block NSUInteger numberOfRetries = 3;
    __block __weak void (^weakSendRequestBlock)(void);
    void (^sendRequestBlock)(void);
    weakSendRequestBlock = sendRequestBlock = ^{
        __strong typeof (weakSendRequestBlock)strongSendRequestBlock = weakSendRequestBlock;
        numberOfRetries--;

        AFHTTPRequestOperation *operation = [self.httpManager HTTPRequestOperationWithRequest:request success:successBlock failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            NSInteger statusCode = [[[error userInfo] objectForKey:AFNetworkingOperationFailingURLResponseErrorKey] statusCode];

            if (numberOfRetries > 0 && (statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 0)) {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                    strongSendRequestBlock();
                });
            } else {
                if (failureBlock) {
                    failureBlock(operation, error);
                }
            }
        }];

        [self.httpManager.operationQueue addOperation:operation];
    };

    sendRequestBlock();
}

使用例:

- (void)getSomeDetails:(DictionaryResultBlock)block
    {
        if (!block) {
            return;
        }

        NSString *urlString = @"your url string";
        NSMutableURLRequest *request = [self.httpManager.requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:urlString relativeToURL:self.defaultUrl] absoluteString] parameters:nil error:nil];

        // Configure you request here
       [request setValue:version forHTTPHeaderField:@"client-version"];

        NSMutableDictionary *bodyParams = @{};   
        [request setHTTPBody:[NSJSONSerialization dataWithJSONObject:bodyParams options:0 error:nil]];

        [self sendRequest:request successBlock:^(AFHTTPRequestOperation *operation, id responseObject) {
            id response = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil];
            block(response, nil);
        } failureBlock:^(AFHTTPRequestOperation *operation, NSError *error) {
            block(nil, error);
        }];
    }
于 2014-08-07T08:53:17.453 に答える
2

私の場合、頻繁に再試行機能が必要だったので、 AFNetworking+RetryPolicyに役立つこの再試行ポリシーカテゴリを考え出しました。

AFNetworking 3.0に関しては、うまく機能する可能性があります。

于 2016-08-26T23:34:17.257 に答える
1

あなたの答えに基づいて、ブロックをパラメーターとして受け取るブロックを使用することで、さらに一般的な(そしてトリッキーな)ことを行うことができます。

typedef void (^CallbackBlock)(NSError* error, NSObject* response);
- (void) performBlock:(void (^)(CallbackBlock callback)) blockToExecute retryingNumberOfTimes:(NSUInteger)ntimes onCompletion:(void (^)(NSError* error, NSObject* response)) onCompletion {
    blockToExecute(^(NSError* error, NSObject* response){
        if (error == nil) {
            onCompletion(nil, response);
        } else {
            if (ntimes <= 0) {
                if (onCompletion) {
                    onCompletion(error, nil);
                }
            } else {
                [self performBlock:blockToExecute retryingNumberOfTimes:(ntimes - 1) onCompletion:onCompletion];
            }
        };
    });
}

次に、非同期 HTTP リクエストを次のように囲みます。

[self performBlock:^(CallbackBlock callback) {

        [...]
        AFHTTPRequestOperationManager *manager = [WSManager getHTTPRequestOperationManager];
        [manager POST:base parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {

            dispatch_async(dispatch_get_main_queue(), ^(void){
                    if (callback) {
                        callback(nil, responseObject);
                    }
                });

            } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                if (callback) {
                    NSError* errorCode = [[NSError alloc] initWithDomain:AppErrorDomain code:[operation.response statusCode] userInfo:@{ NSLocalizedDescriptionKey :error.localizedDescription}];
                    callback(errorCode, nil);
                }
            }];

    } retryingNumberOfTimes:5 onCompletion:^(NSError *error,  NSObject* response) {
        //everything done
    }];

この方法では、再試行は HTTP 要求が完了するまで待機するため、各要求メソッドに再試行ループを実装する必要はありません。

于 2016-03-07T10:04:27.037 に答える