2012-08-31 15 views
20

Recientemente he migrado de ASIHTTPRequest a AFNetworking, que ha sido excelente. Sin embargo, el servidor con el que me estoy conectando tiene algunos problemas y algunas veces hace que mis solicitudes de tiempo de espera excedan el tiempo. Al utilizar ASIHTTPRequest era posible configurar una cuenta de reintentos de una solicitud en el caso de un tiempo de espera utilizando el siguiente selector deAFNetworking: cómo configurar las solicitudes para volver a intentarlas en caso de que se agote el tiempo de espera?

-setNumberOfTimesToRetryOnTimeout: 

Esto se puede hacer referencia más adelante en este post, Can an ASIHTTPRequest be retried?

Esto se AFNetworking si no están familiarizados https://github.com/AFNetworking/AFNetworking#readme

he podido encontrar una API equivalente en AFNetworking, alguien ha encontrado una solución para volver a intentar las solicitudes de red en caso de tiempo de espera utilizando AFNetworking?

Respuesta

56

Matt Thompson desarrollador de AFNetworking tuvo la amabilidad de responder a esto por mí. A continuación se muestra el enlace de Github que explica la solución.

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

Básicamente, AFNetworking no admite esta funcionalidad. Se deja a los desarrolladores para implementar en una base de caso por caso, como se muestra a continuación (tomado de la respuesta de Matt Thompson en github)

- (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]; 
     }]; 
    } 
} 
+0

Creo que esto debería ser "if (ntimes <= 0) {if (failure) {...} else if (success) {success (...):} de lo contrario no habrá respuesta si esto fue exitoso en el último intento – GK100

+0

en AFNetworking3.0 AFHTTPRequestOperation ha eliminado cómo debería implementar este código con AFNetworking3.0 ???? – do01

2

he implementado método privado en mi clase 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(); 
} 

Ejemplo de uso:

- (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); 
     }]; 
    } 
0

con base en sus respuestas, se podría hacer algo aún más genérica (y complicado) mediante el uso de un bloque tomando como parámetro de un bloque:

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]; 
      } 
     }; 
    }); 
} 

Entonces rodear sus peticiones HTTP asíncronas como el siguiente:

[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 
    }]; 

De esta manera los reintentos esperar a que la petición HTTP a fin y que no tiene que implementar el bucle de reintento en cada uno métodos de petición.

2

En mi caso, con frecuencia requiere la funcionalidad de reintento por lo que se me ocurrió este ingenio política de reintento categoría que le ayudará con que AFNetworking+RetryPolicy

Con respecto a AFNetworking 3.0 podría servir también.

+0

Esto es simplemente épico. Exactamente lo que estaba buscando, ¡gracias! – Tander

Cuestiones relacionadas