iOS: ¿cómo realizar una solicitud HTTP POST?

128

Me estoy acercando al desarrollo de iOS y me gustaría tener una de mis primeras aplicaciones para realizar una solicitud HTTP POST.

Hasta donde puedo entender, debo administrar la conexión que maneja la solicitud a través de un NSURLConnectionobjeto, lo que me obliga a tener un objeto delegado, que a su vez manejará eventos de datos.

¿Podría alguien aclarar la tarea con un ejemplo práctico?

Debería contactar a un punto final https enviando datos de autenticación (nombre de usuario y contraseña) y obteniendo una respuesta de texto sin formato.

Federico Zancan
fuente

Respuestas:

167

Puede usar NSURLConnection de la siguiente manera:

  1. Configure su NSURLRequest: Use requestWithURL:(NSURL *)theURLpara inicializar la solicitud.

    Si necesita especificar una solicitud POST y / o encabezados HTTP, use NSMutableURLRequestcon

    • (void)setHTTPMethod:(NSString *)method
    • (void)setHTTPBody:(NSData *)data
    • (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
  2. Envíe su solicitud de 2 maneras usando NSURLConnection:

    • Sincrónicamente: (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error

      Esto devuelve una NSDatavariable que puede procesar.

      IMPORTANTE: recuerde iniciar la solicitud síncrona en un hilo separado para evitar bloquear la interfaz de usuario.

    • Asincrónicamente: (void)start

No olvide configurar el delegado de NSURLConnection para manejar la conexión de la siguiente manera:

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

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

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Error", @"")
                                 message:[error localizedDescription]
                                delegate:nil
                       cancelButtonTitle:NSLocalizedString(@"OK", @"") 
                       otherButtonTitles:nil] autorelease] show];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSString *responseText = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];

    // Do anything you want with it 

    [responseText release];
}

// Handle basic authentication challenge if needed
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
    NSString *username = @"username";
    NSString *password = @"password";

    NSURLCredential *credential = [NSURLCredential credentialWithUser:username
                                                             password:password
                                                          persistence:NSURLCredentialPersistenceForSession];
    [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
}
Anh Do
fuente
44
Apple dice que el uso de solicitudes sincrónicas "no es recomendable" developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/... aunque si sabe lo suficiente como para perder el tiempo con diferentes hilos, probablemente estará bien.
Aaron Brown
@Anh Nice Answer, pero estaba un poco escéptico con el último método didReceiveAuthenticationChallenge. ¿Hay algún problema de seguridad con las contraseñas / nombres de usuario de codificación rígida? ¿Hay alguna forma de evitar esto?
Sam Spencer el
2
En general, almacenaría las credenciales en el llavero y las recuperaría allí para manejar la autenticación básica.
Anh Do
2
iOS 5 en adelante también se puede utilizar + (void) sendAsynchronousRequest: (NSURLRequest ) la cola de solicitudes: (NSOperationQueue *) cola completionHandler: (void (^) (NSURLResponse , NSData *, NSError *)) manejador
chunkyguy
13

EDITAR: ASIHTTPRequest ha sido abandonado por el desarrollador. Sigue siendo realmente una buena OMI, pero probablemente deberías buscar en otro lado ahora.

Recomiendo encarecidamente utilizar la biblioteca ASIHTTPRequest si está manejando HTTPS. Incluso sin https, proporciona un envoltorio realmente agradable para cosas como esta y, aunque no es difícil hacerlo por http simple, creo que la biblioteca es agradable y una excelente manera de comenzar.

Las complicaciones HTTPS están lejos de ser triviales en varios escenarios, y si desea ser robusto en el manejo de todas las variaciones, encontrará que la biblioteca ASI es una verdadera ayuda.

Roger
fuente
13
La biblioteca ASIHTTPRequest ha sido oficialmente abandonada por su desarrollador, ya que esta publicación dice: allseeing-i.com/[request_release] ; , Le recomendaría que use otras bibliotecas como sugiere el desarrollador, o incluso mejor, intente aprender NSURLRequest :) Cheers.
Goles
@ Mr.Gando, su enlace no parece funcionar, tenga en cuenta que el punto y coma es significativo. Dicho eso, MUY triste verlo abandonado. Realiza muchas de las cosas de autenticación realmente bien y es mucho trabajo replicarlo todo ... lástima ...
Roger
Y ese enlace tampoco funciona. Para cualquiera que intente encontrarlo, tenga en cuenta que la url correcta requiere un punto y coma al final, SO está causando el; para ser excluido de los enlaces que las personas están publicando.
Roger
3
AFNetworking es lo que la mayoría de la gente parece estar usando ahora.
Vadoff
7

Pensé que actualizaría esta publicación un poco y diría que gran parte de la comunidad de iOS se mudó a AFNetworking después de que ASIHTTPRequestfue abandonada. Lo recomiendo altamente. Es un excelente contenedor NSURLConnectiony permite llamadas asincrónicas, y básicamente cualquier cosa que pueda necesitar.

Jesse Naugher
fuente
2
Sé que la respuesta aceptada es buena, no significa comportamiento ni nada, pero esto definitivamente debería tener más votos a favor. ¿Quizás si se agrega un ejemplo y algún fragmento de código, como sugiere la pregunta?
acrespo
6

Aquí hay una respuesta actualizada para iOS7 +. Utiliza NSURLSession, el nuevo atractivo. Descargo de responsabilidad, esto no se ha probado y se escribió en un campo de texto:

- (void)post {
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://example.com/dontposthere"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    // Uncomment the following two lines if you're using JSON like I imagine many people are (the person who is asking specified plain text)
    // [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    // [request addValue:@"application/json" forHTTPHeaderField:@"Accept"]; 
    [request setHTTPMethod:@"POST"];
    NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }];
    [postDataTask resume];
}

-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(    NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
}

O mejor aún, use AFNetworking 2.0+. Por lo general, subclase AFHTTPSessionManager, pero pongo todo esto en un método para tener un ejemplo conciso.

- (void)post {
    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://example.com"]];
    // Many people will probably want [AFJSONRequestSerializer serializer];
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    // Many people will probably want [AFJSONResponseSerializer serializer];
    manager.responseSerializer = [AFHTTPRequestSerializer serializer];
    manager.securityPolicy.allowInvalidCertificates = NO; // Some servers require this to be YES, but default is NO.
    [manager.requestSerializer setAuthorizationHeaderFieldWithUsername:@"username" password:@"password"];
    [[manager POST:@"dontposthere" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        NSLog(@"darn it");
    }] resume];
}

Si está utilizando el serializador de respuestas JSON, el responseObject será objeto de la respuesta JSON (a menudo NSDictionary o NSArray).

Kyle Robson
fuente
1

NOTA: Ejemplo de Pure Swift 3 (Xcode 8): Pruebe el siguiente código de muestra. Es el simple ejemplo de dataTaskfunción de URLSession.

func simpleDataRequest() {

        //Get the url from url string
        let url:URL = URL(string: "YOUR URL STRING")!

        //Get the session instance
        let session = URLSession.shared

        //Create Mutable url request
        var request = URLRequest(url: url as URL)

        //Set the http method type
        request.httpMethod = "POST"

        //Set the cache policy
        request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringCacheData

        //Post parameter
        let paramString = "key=value"

        //Set the post param as the request body
        request.httpBody = paramString.data(using: String.Encoding.utf8)

        let task = session.dataTask(with: request as URLRequest) {
            (data, response, error) in

            guard let _:Data = data as Data?, let _:URLResponse = response  , error == nil else {

                //Oops! Error occured.
                print("error")
                return
            }

            //Get the raw response string
            let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))

            //Print the response
            print(dataString!)

        }

        //resume the task
        task.resume()

    }
Dinesh
fuente
0

Xcode 8 y Swift 3.0

Usando URLSession:

 let url = URL(string:"Download URL")!
 let req = NSMutableURLRequest(url:url)
 let config = URLSessionConfiguration.default
 let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)

 let task : URLSessionDownloadTask = session.downloadTask(with: req as URLRequest)
task.resume()

Llamada de delegado de URLSession:

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {

}


func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, 
didWriteData bytesWritten: Int64, totalBytesWritten writ: Int64, totalBytesExpectedToWrite exp: Int64) {
                   print("downloaded \(100*writ/exp)" as AnyObject)

}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){

}

Usando el bloque GET / POST / PUT / DELETE:

 let request = NSMutableURLRequest(url: URL(string: "Your API URL here" ,param: param))!,
        cachePolicy: .useProtocolCachePolicy,
        timeoutInterval:"Your request timeout time in Seconds")
    request.httpMethod = "GET"
    request.allHTTPHeaderFields = headers as? [String : String] 

    let session = URLSession.shared

    let dataTask = session.dataTask(with: request as URLRequest) {data,response,error in
        let httpResponse = response as? HTTPURLResponse

        if (error != nil) {
         print(error)
         } else {
         print(httpResponse)
         }

        DispatchQueue.main.async {
           //Update your UI here
        }

    }
    dataTask.resume()

Funciona bien para mí ... pruébalo 100% de garantía de resultados

Saumil Shah
fuente
0

Así es como funciona la solicitud POST HTTP para iOS 8+ usando NSURLSession:

- (void)call_PostNetworkingAPI:(NSURL *)url withCompletionBlock:(void(^)(id object,NSError *error,NSURLResponse *response))completion
{
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
    config.URLCache = nil;
    config.timeoutIntervalForRequest = 5.0f;
    config.timeoutIntervalForResource =10.0f;
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
    NSMutableURLRequest *Req=[NSMutableURLRequest requestWithURL:url];
    [Req setHTTPMethod:@"POST"];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:Req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error == nil) {

            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
            if (dict != nil) {
                completion(dict,error,response);
            }
        }else
        {
            completion(nil,error,response);
        }
    }];
    [task resume];

}

Espero que esto satisfaga su siguiente requisito.

Abilash Balasubramanian
fuente