AFNetworking 2.0 agrega encabezados a la solicitud GET

78

Acabo de comenzar a usar AFNetworking 2.0 y me preguntaba cómo coloco encabezados en una solicitud HTTP Get. La documentación configura un GET como este:

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = @{@"foo": @"bar"};
[manager POST:@"http://example.com/resources.json" parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"Error: %@", error);
}];

Pero como no estamos manejando, NSURLRequestsno estoy seguro de cómo configurar los encabezados HTTP.

Cualquier ayuda sería apreciada.
Saludos,
Mike

Mackey18
fuente
3
@RyanR no es un duplicado. Eso es de AFN 1.x no 2.0. Todo ha cambiado por completo desde entonces.
Mackey
De acuerdo con la guía de migración, AFHttpClient todavía existe y todavía tiene la responsabilidad de ejecutar solicitudes Http para su aplicación
RyanR
2
¿Un proyecto de código abierto con documentación conflictiva? NEVAR. AFHTTPClient -> AFHTTPSessionManager aparentemente. Tienes razón, el uso setDefaultHeaderapesta. Una búsqueda de 30 segundos de los documentos y encuentro que AFHTTPRequestSerializer es responsable de HTTPHeaders por solicitud.
RyanR
2
¡@RyanR D'oh se perdió eso! Encontré el método correcto gracias a su hallazgo:manager.requestSerializer setValue:<#(NSString *)#> forHTTPHeaderField:<#(NSString *)#>
Mackey18

Respuestas:

84

La fantástica documentación de AFNetworking 2.0 hace que sea un poco difícil de encontrar, pero está ahí. En el AFHTTPRequestSerializeres -setValue:forHTTPHeaderField:.

Alternativamente, si sigue su enfoque recomendado de crear un administrador de sesión que se derive de AFHTTPSessionManageresa clase, esa clase puede anular un método para modificar los encabezados en cada solicitud -dataTaskWithRequest:completionHandler:. Utilizo esto para inspeccionar las solicitudes y modificar los encabezados caso por caso, y lo prefiero a modificar el serializador, ya que mantiene la responsabilidad de la red contenida en ese administrador (y evita la contaminación con singletons)

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLResponse *, id, NSError *))completionHandler
{
    static NSString *deviceId;
    if(!deviceId)
    {
        deviceId = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    }

    NSMutableURLRequest *req = (NSMutableURLRequest *)request;
    // Give each request a unique ID for tracing
    NSString *reqId = [NSString stringWithFormat:@"%@+%@", deviceId, [[NSUUID UUID] UUIDString] ];
    [req setValue:reqId forHTTPHeaderField:"x-myapp-requestId"];
    return [super dataTaskWithRequest:req completionHandler:completionHandler];
}
RyanR
fuente
4
¿Le importaría agregar un fragmento de cómo se usaría esto? Soy un tipo de Android que intenta aprender iOS y no estoy muy seguro de cómo usar esta respuesta.
ChuckKelly
1
pero ¿por qué se establecen los encabezados en el serializador? También noté que de esta manera son persistentes, por lo que una vez que estableces un campo de encabezado, ¡se quedará allí para siempre! (Tuve que agregar mi propio método para eliminarlos)
Peter Lapisu
4
@PeterLapisu llamando a este método con nilfor valuelo eliminará. No debería usar encabezados para pasar valores efímeros del cliente al servidor, eso es parte de su carga útil. Los encabezados son para valores que no cambian con frecuencia, como datos de autenticación, control de caché, etc. Algunos servidores incluso determinarán si deben devolver los datos almacenados en caché basándose en la coincidencia de los parámetros del encabezado, lo que no querrá evitar accidentalmente. Si necesita establecer encabezados solo para solicitudes individuales, sospecho que son datos que pertenecen a la carga útil.
RyanR
2
@PeterLapisu No sé cuál fue el razonamiento de los equipos de AFN, pero definitivamente tiene sentido desde una perspectiva de diseño. El serializador es lo que sabe cómo codificar / decodificar la solicitud y la respuesta para cualquier protocolo que esté utilizando. Si fuera por FTP, serializaría esa información de manera diferente que a través de una cola de mensajes. De esta manera, solo el componente responsable de ese tipo de transporte conoce los detalles del transporte (esto es parte de [encapsulación] ( en.wikipedia.org/wiki/… )
RyanR
2
todavía creo que es un problema de mal diseño github.com/AFNetworking/AFNetworking/issues/1793
Peter Lapisu
146

Aquí hay un ejemplo con AFNetworking 2.0

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:@"calvinAndHobbesRock" forHTTPHeaderField:@"X-I do what I want"];

[manager GET:@"http://localhost:3000" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];

La clave son las siguientes 2 líneas:

manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:@"calvinAndHobbessRock" forHTTPHeaderField:@"X-I-do-what-I-want"];
Shaheen Ghiassy
fuente
1
Gracias por el ejemplo !!
diegomen
17

Agregar respuesta y serializador de solicitud resolvió mi problema.

manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.requestSerializer = [AFJSONRequestSerializer serializer];

[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
usuario2073541
fuente
7

He utilizado este formulario para concertar una cita con un encabezado específico.

AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[operationManager.requestSerializer setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];

[operationManager POST:url
            parameters:params
               success:^(AFHTTPRequestOperation *operation, id responseObject) {

                   if (success) {
                       success(responseObject);
                   }

               }
               failure:^(AFHTTPRequestOperation *operation, NSError *error) {

                   NSLog(@"Error: %@", [error description]);

               }
 ];
Carlos Avalos
fuente
¿No crees que estos encabezados se enviarán con cualquier solicitud desde el momento en que los hayas configurado?
vahotm
6

Esto es lo que creo que es la mejor opción. En un singleton en algún lugar, configure un AFHTTPSessionManagerusando un NSURLSessionConfiguration, y luego utilícelo AFHTTPSessionManagercada vez que desee realizar una solicitud.

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPAdditionalHeaders = @{@"Accepts": @"application/json"};

mySingletonSessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:kMyBaseUrl] sessionConfiguration:config];
Chris
fuente
3

Hice esto ... para aquellos que están pasando token

[manager.requestSerializer setValue:[NSString stringWithFormat:@"Token token=\"%@\"", _userObj.oAuth] forHTTPHeaderField:@"Authorization"];
Serge Pedroza
fuente
-3

Utilice el siguiente código para poner cualquier tipo de valor de encabezado:

[[FRHTTPReqManager sharedManager].requestSerializer setValue:value forHTTPHeaderField:key];
Evana
fuente