AFNetworking 2.0 cuerpo de request multiparte en blanco

Similar a este problema .

Usando AFNetworking 2.0.3 e intentando cargar una image usando AFHTTPSessionManager's POST + constructingBodyWithBlock. Por razones desconocidas, parece que el cuerpo de la publicación HTTP siempre está en blanco cuando se realiza la request al server.

Subclasss AFHTTPSessionManager a continuación (de ahí el uso de [self POST ...] .

He intentado build la request de dos maneras.

Método 1 : Acabo de intentar pasar parameters y luego agregar solo los datos de la image en caso de existir.

 - (void) createNewAccount:(NSString *)nickname accountType:(NSInteger)accountType primaryPhoto:(UIImage *)primaryPhoto { NSString *accessToken = self.accessToken; // Ensure none of the params are nil, otherwise it'll mess up our dictionary if (!nickname) nickname = @""; if (!accessToken) accessToken = @""; NSDictionary *params = @{@"nickname": nickname, @"type": [[NSNumber alloc] initWithInteger:accountType], @"access_token": accessToken}; NSLog(@"Creating new account %@", params); [self POST:@"accounts" parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { if (primaryPhoto) { [formData appendPartWithFileData:UIImageJPEGRepresentation(primaryPhoto, 1.0) name:@"primary_photo" fileName:@"image.jpg" mimeType:@"image/jpeg"]; } } success:^(NSURLSessionDataTask *task, id responseObject) { NSLog(@"Created new account successfully"); } failure:^(NSURLSessionDataTask *task, NSError *error) { NSLog(@"Error: couldn't create new account: %@", error); }]; } 

Método 2 : intentó build los datos del formulario en el bloque mismo:

 - (void) createNewAccount:(NSString *)nickname accountType:(NSInteger)accountType primaryPhoto:(UIImage *)primaryPhoto { // Ensure none of the params are nil, otherwise it'll mess up our dictionary if (!nickname) nickname = @""; NSLog(@"Creating new account %@", params); [self POST:@"accounts" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { [formData appendPartWithFormData:[nickname dataUsingEncoding:NSUTF8StringEncoding] name:@"nickname"]; [formData appendPartWithFormData:[NSData dataWithBytes:&accountType length:sizeof(accountType)] name:@"type"]; if (self.accessToken) [formData appendPartWithFormData:[self.accessToken dataUsingEncoding:NSUTF8StringEncoding] name:@"access_token"]; if (primaryPhoto) { [formData appendPartWithFileData:UIImageJPEGRepresentation(primaryPhoto, 1.0) name:@"primary_photo" fileName:@"image.jpg" mimeType:@"image/jpeg"]; } } success:^(NSURLSessionDataTask *task, id responseObject) { NSLog(@"Created new account successfully"); } failure:^(NSURLSessionDataTask *task, NSError *error) { NSLog(@"Error: couldn't create new account: %@", error); }]; } 

Al usar cualquier método, cuando la request HTTP llega al server, no hay datos POST ni parameters de cadena de consulta, solo encabezados HTTP.

 Transfer-Encoding: Chunked Content-Length: User-Agent: MyApp/1.0 (iPhone Simulator; iOS 7.0.3; Scale/2.00) Connection: keep-alive Host: 127.0.0.1:5000 Accept: */* Accept-Language: en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5 Content-Type: multipart/form-data; boundary=Boundary+0xAbCdEfGbOuNdArY Accept-Encoding: gzip, deflate 

¿Alguna idea? También publicó un error en el github repo de AFNetworking .

Rob tiene toda la razón, el problema que estás viendo está relacionado con el problema (ahora cerrado) 1398 . Sin embargo, quería proporcionar un post rápido en caso de que alguien más estuviera buscando.

Primero, aquí hay un fragment de código proporcionado por gberginc en github que puede modelar el file subido después de:

 NSString* apiUrl = @"http://example.com/upload"; // Prepare a temporary file to store the multipart request prior to sending it to the server due to an alleged // bug in NSURLSessionTask. NSString* tmpFilename = [NSString stringWithFormat:@"%f", [NSDate timeIntervalSinceReferenceDate]]; NSURL* tmpFileUrl = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmpFilename]]; // Create a multipart form request. NSMutableURLRequest *multipartRequest = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:apiUrl parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) { [formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@"file" fileName:fileName mimeType:@"image/jpeg" error:nil]; } error:nil]; // Dump multipart request into the temporary file. [[AFHTTPRequestSerializer serializer] requestWithMultipartFormRequest:multipartRequest writingStreamContentsToFile:tmpFileUrl completionHandler:^(NSError *error) { // Once the multipart form is serialized into a temporary file, we can initialize // the actual HTTP request using session manager. // Create default session manager. AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; // Show progress. NSProgress *progress = nil; // Here note that we are submitting the initial multipart request. We are, however, // forcing the body stream to be read from the temporary file. NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:multipartRequest fromFile:tmpFileUrl progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { // Cleanup: remove temporary file. [[NSFileManager defaultManager] removeItemAtURL:tmpFileUrl error:nil]; // Do something with the result. if (error) { NSLog(@"Error: %@", error); } else { NSLog(@"Success: %@", responseObject); } }]; // Add the observer monitoring the upload progress. [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:NULL]; // Start the file upload. [uploadTask resume]; }]; 

Y en segundo lugar, para resumir el problema (y por qué tiene que usar un file temporal como un trabajo), realmente es doble.

  1. Apple considera que el encabezado de longitud de contenido está bajo su control y cuando se establece una secuencia de cuerpo HTTP para una NSURLRequest las bibliotecas de Apple establecerán la encoding en Chunked y luego abandonarán ese encabezado (y, por lo tanto, borrarán cualquier valor de longitud de contenido AFNetworking sets)
  2. El server al que se está cargando la carga no es compatible Transfer-Encoding: Chunked (por ejemplo, S3)

Pero resulta que, si está cargando una request desde un file (porque el tamaño total de la request se conoce con anterioridad), las bibliotecas de Apple configurarán correctamente el encabezado de longitud de contenido. Loco, ¿verdad?

Si se profundiza en esto, parece que cuando se utiliza NSURLSession junto con setHTTPBodyStream , incluso si la request establece Content-Length (que AFURLRequestSerialization hace en requestByFinalizingMultipartFormData ), ese encabezado no se envía. Puede confirmar esto comparando allHTTPHeaderFields de originalRequest y currentRequest de la tarea. También confirmé esto con Charles.

Lo interesante es que Transfer-Encoding se está configurando como chunked (lo cual es correcto en general cuando se desconoce la longitud).

En resumen, esto parece ser una manifestación de la elección de AFNetworking para usar setHTTPBodyStream lugar de setHTTPBody (que no sufre este comportamiento), que, cuando se combina con NSURLSession da NSURLSession resultado este comportamiento de requestes mal formadas.

Creo que esto está relacionado con el problema de AFNetworking 1398 .

Me estaba encontrando con este problema yo mismo, y estaba probando ambos methods y el método sugerido aquí …

Resulta que fue tan simple como cambiar la key de "nombre" de datos adjuntos a "file" en lugar de la variable de nombre de file.

Asegúrese de que su key de datos coincida, o verá el mismo síntoma de un set de datos vacío.