Cómo enviar datos json en la solicitud Http usando NSURLRequest


80

Soy nuevo en el objetivo-c y estoy empezando a esforzarme mucho en la solicitud / respuesta recientemente. Tengo un ejemplo funcional que puede llamar a una URL (a través de http GET) y analizar el json devuelto.

El ejemplo de trabajo de esto está a continuación.

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [responseData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
  NSLog([NSString stringWithFormat:@"Connection failed: %@", [error description]]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    [connection release];
  //do something with the json that comes back ... (the fun part)
}

- (void)viewDidLoad
{
  [self searchForStuff:@"iPhone"];
}

-(void)searchForStuff:(NSString *)text
{
  responseData = [[NSMutableData data] retain];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.whatever.com/json"]];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];
}

Mi primera pregunta es: ¿se ampliará este enfoque? ¿O no es asincrónico (lo que significa que bloqueo el hilo de la interfaz de usuario mientras la aplicación está esperando la respuesta)

Mi segunda pregunta es: ¿cómo puedo modificar la parte de solicitud de esto para hacer una POST en lugar de GET? ¿Es simplemente modificar el HttpMethod así?

[request setHTTPMethod:@"POST"];

Y finalmente, ¿cómo agrego un conjunto de datos json a esta publicación como una cadena simple (por ejemplo)

{
    "magic":{
               "real":true
            },
    "options":{
               "happy":true,
                "joy":true,
                "joy2":true
              },
    "key":"123"
}

Gracias de antemano


Respuestas:


105

Esto es lo que hago (tenga en cuenta que el JSON que va a mi servidor debe ser un diccionario con un valor (otro diccionario) para key = question..ie {: question => {dictionary}}):

NSArray *objects = [NSArray arrayWithObjects:[[NSUserDefaults standardUserDefaults]valueForKey:@"StoreNickName"],
  [[UIDevice currentDevice] uniqueIdentifier], [dict objectForKey:@"user_question"],     nil];
NSArray *keys = [NSArray arrayWithObjects:@"nick_name", @"UDID", @"user_question", nil];
NSDictionary *questionDict = [NSDictionary dictionaryWithObjects:objects forKeys:keys];

NSDictionary *jsonDict = [NSDictionary dictionaryWithObject:questionDict forKey:@"question"];

NSString *jsonRequest = [jsonDict JSONRepresentation];

NSLog(@"jsonRequest is %@", jsonRequest);

NSURL *url = [NSURL URLWithString:@"https://xxxxxxx.com/questions"];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
             cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];


NSData *requestData = [jsonRequest dataUsingEncoding:NSUTF8StringEncoding];

[request setHTTPMethod:@"POST"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:[NSString stringWithFormat:@"%d", [requestData length]] forHTTPHeaderField:@"Content-Length"];
[request setHTTPBody: requestData];

NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if (connection) {
 receivedData = [[NSMutableData data] retain];
}

Los datos recibidos son luego manejados por:

NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [jsonString JSONValue];
NSDictionary *question = [jsonDict objectForKey:@"question"];

Esto no está 100% claro y tomará una nueva lectura, pero todo debería estar aquí para comenzar. Y por lo que puedo decir, esto es asincrónico. Mi interfaz de usuario no está bloqueada mientras se realizan estas llamadas. Espero que ayude.


Todo se ve bien excepto esta línea [dict objectForKey: @ "user_question"], nil]; - dict no está declarado en su muestra. ¿Es esto solo un diccionario simple o algo especial?
Toran Billups

1
Lo siento por eso. Sí, "dict" es solo un diccionario simple que cargo de los documentos de los usuarios de iOS.
Mike G

19
Esto está usando el NSDictionarymétodo de instancia JSONRepresentation. Podría sugerir que use el NSJSONSerializationmétodo de clase dataWithJSONObject, en lugar del json-framework .
Rob

Es más eficiente convertir NSUInteger a NSString a través de un NSNumber como [[NSNumber numberWithUnsignedInt:requestData.length] stringValue].
respectTheCode

1
@MikeG Se corrigió un error de larga data y hasta ahora inadvertido en el código de muestra. Lo siento, por editar su publicación;)
CouchDeveloper

7

Luché con esto por un tiempo. Ejecutando PHP en el servidor. Este código publicará un json y obtendrá la respuesta json del servidor

NSURL *url = [NSURL URLWithString:@"http://example.co/index.php"];
NSMutableURLRequest *rq = [NSMutableURLRequest requestWithURL:url];
[rq setHTTPMethod:@"POST"];
NSString *post = [NSString stringWithFormat:@"command1=c1&command2=c2"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding];
[rq setHTTPBody:postData];
[rq setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[NSURLConnection sendAsynchronousRequest:rq queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
 {
     if ([data length] > 0 && error == nil){
         NSError *parseError = nil;
         NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
         NSLog(@"Server Response (we want to see a 200 return code) %@",response);
         NSLog(@"dictionary %@",dictionary);
     }
     else if ([data length] == 0 && error == nil){
         NSLog(@"no data returned");
         //no data, but tried
     }
     else if (error != nil)
     {
         NSLog(@"there was a download error");
         //couldn't download

     }
 }];

1
content type = "application / x-www-form-urlencoded" hizo el truco. Gracias
SamChen

Buena respuesta. Usé "application / json" en mi caso
Gajendra K Chauhan

6

Sugeriría usar ASIHTTPRequest

ASIHTTPRequest es un contenedor fácil de usar de la API de CFNetwork que facilita algunos de los aspectos más tediosos de la comunicación con los servidores web. Está escrito en Objective-C y funciona tanto en aplicaciones de Mac OS X como de iPhone.

Es adecuado para realizar solicitudes HTTP básicas e interactuar con servicios basados ​​en REST (GET / POST / PUT / DELETE). La subclase ASIFormDataRequest incluida facilita el envío de datos y archivos POST utilizando datos de formularios / multiparte.


Tenga en cuenta que el autor original interrumpió este proyecto. Consulte la siguiente publicación para conocer las razones y las alternativas: http://allseeing-i.com/%5Brequest_release%5D ;

Personalmente soy un gran fan de AFNetworking


3

La mayoría de ustedes ya saben esto por ahora, pero estoy publicando esto, por si acaso, algunos de ustedes todavía están luchando con JSON en iOS6 +.

En iOS6 y posteriores, tenemos la clase NSJSONSerialization que es rápida y no depende de la inclusión de bibliotecas "externas".

NSDictionary *result = [NSJSONSerialization JSONObjectWithData:[resultStr dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; 

Esta es la forma en que iOS6 y versiones posteriores ahora pueden analizar JSON de manera eficiente. El uso de SBJson también es una implementación previa a ARC y también trae consigo esos problemas si está trabajando en un entorno ARC.

¡Espero que esto ayude!


2

Aquí hay un gran artículo usando Restkit

Explica cómo serializar datos anidados en JSON y adjuntar los datos a una solicitud HTTP POST.


2

Dado que mi edición de la respuesta de Mike G para modernizar el código fue rechazada 3 a 2 como

Esta edición estaba destinada a dirigirse al autor de la publicación y no tiene sentido como edición. Debería haber sido escrito como comentario o respuesta.

Estoy volviendo a publicar mi edición como una respuesta separada aquí. Esta edición elimina la JSONRepresentationdependencia con NSJSONSerializationcomo sugiere el comentario de Rob con 15 votos a favor.

    NSArray *objects = [NSArray arrayWithObjects:[[NSUserDefaults standardUserDefaults]valueForKey:@"StoreNickName"],
      [[UIDevice currentDevice] uniqueIdentifier], [dict objectForKey:@"user_question"],     nil];
    NSArray *keys = [NSArray arrayWithObjects:@"nick_name", @"UDID", @"user_question", nil];
    NSDictionary *questionDict = [NSDictionary dictionaryWithObjects:objects forKeys:keys];

    NSDictionary *jsonDict = [NSDictionary dictionaryWithObject:questionDict forKey:@"question"];

    NSLog(@"jsonRequest is %@", jsonRequest);

    NSURL *url = [NSURL URLWithString:@"https://xxxxxxx.com/questions"];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
                 cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];


    NSData *requestData = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil]; //TODO handle error

    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%d", [requestData length]] forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBody: requestData];

    NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
    if (connection) {
     receivedData = [[NSMutableData data] retain];
    }

Los datos recibidos son luego manejados por:

NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    NSDictionary *question = [jsonDict objectForKey:@"question"];

0

Aquí hay un ejemplo actualizado que usa NSURLConnection + sendAsynchronousRequest: (10.7+, iOS 5+), La solicitud "Publicar" sigue siendo la misma que con la respuesta aceptada y se omite aquí para mayor claridad:

NSURL *apiURL = [NSURL URLWithString:
    [NSString stringWithFormat:@"http://www.myserver.com/api/api.php?request=%@", @"someRequest"]];
NSURLRequest *request = [NSURLRequest requestWithURL:apiURL]; // this is using GET, for POST examples see the other answers here on this page
[NSURLConnection sendAsynchronousRequest:request
                                   queue:[NSOperationQueue mainQueue]
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
     if(data.length) {
         NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
         if(responseString && responseString.length) {
             NSLog(@"%@", responseString);
         }
     }
}];

la pregunta era sobre POST
ahmad

2
no, la primera parte de la pregunta es sobre la asincronicidad y no hay una respuesta aquí que responda eso. Saludos por el voto negativo.
auco

0

Puedes probar este código para enviar una cadena json

NSData *jsonData = [NSJSONSerialization dataWithJSONObject:ARRAY_CONTAIN_JSON_STRING options:NSJSONWritin*emphasized text*gPrettyPrinted error:NULL];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *WS_test = [NSString stringWithFormat:@"www.test.com?xyz.php&param=%@",jsonString];
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.