iOS: como executar uma solicitação HTTP POST?

128

Estou me aproximando do desenvolvimento do iOS e gostaria de ter um dos meus primeiros aplicativos para executar uma solicitação HTTP POST.

Tanto quanto posso entender, eu devo gerenciar a conexão que lida com a solicitação por meio de um NSURLConnectionobjeto, o que me obriga a ter um objeto delegado, que por sua vez lida com eventos de dados.

Alguém poderia esclarecer a tarefa com um exemplo prático?

Devo entrar em contato com um endpoint https enviando dados de autenticação (nome de usuário e senha) e recebendo uma resposta em texto sem formatação.

Federico Zancan
fonte

Respostas:

167

Você pode usar o NSURLConnection da seguinte maneira:

  1. Defina seu NSURLRequest: Use requestWithURL:(NSURL *)theURLpara inicializar a solicitação.

    Se você precisar especificar uma solicitação POST e / ou cabeçalhos HTTP, use NSMutableURLRequestcom

    • (void)setHTTPMethod:(NSString *)method
    • (void)setHTTPBody:(NSData *)data
    • (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
  2. Envie sua solicitação de duas maneiras usando NSURLConnection:

    • Sincronicamente: (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error

      Isso retorna uma NSDatavariável que você pode processar.

      IMPORTANTE: Lembre-se de iniciar a solicitação síncrona em um thread separado para evitar o bloqueio da interface do usuário.

    • Assíncrona: (void)start

Não esqueça de definir o delegado do NSURLConnection para manipular a conexão da seguinte maneira:

- (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
fonte
4
A Apple diz que o uso de solicitações síncronas "não é recomendado" developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… embora, se você souber o suficiente para mexer com diferentes threads, provavelmente ficará bem.
Aaron Brown
@ Nice resposta, mas eu estava um pouco cético com o último método didReceiveAuthenticationChallenge. Existem problemas de segurança com senhas / nomes de usuários codificados? Existe alguma maneira de contornar isso?
Sam Spencer
2
Geralmente, você armazena as credenciais no chaveiro e as recupera lá para manipular a autenticação básica.
você
2
iOS 5 em diante também pode usar + (void) sendAsynchronousRequest: (NSURLRequest ) fila de pedido: (NSOperationQueue *) fila completionHandler: (vazio (^) (NSURLResponse , NSData *, NSError *)) manipulador
chunkyguy
13

EDIT: ASIHTTPRequest foi abandonado pelo desenvolvedor. Ainda é realmente bom IMO, mas você provavelmente deve procurar outro lugar agora.

Eu recomendo usar a biblioteca ASIHTTPRequest se você estiver manipulando HTTPS. Mesmo sem o https, ele fornece um invólucro muito bom para coisas como essa e, embora não seja difícil fazer você mesmo com http simples, acho que a biblioteca é boa e uma ótima maneira de começar.

As complicações do HTTPS estão longe de serem triviais em vários cenários e, se você quiser ser robusto ao lidar com todas as variações, encontrará na biblioteca ASI uma ajuda real.

Roger
fonte
13
A biblioteca ASIHTTPRequest foi oficialmente abandonada pelo desenvolvedor, como afirma este post: allseeing-i.com/[request_release] ; , Recomendo que você use outras bibliotecas, conforme sugerido pelo desenvolvedor, ou melhor ainda, tente aprender NSURLRequest :) Felicidades.
Goles 11/11
@ Mr.Gando - seu link parece não funcionar - observe que o ponto e vírgula é significativo. Dito isto, MUITO triste vê-lo abandonado. Ele faz muitas coisas autenticação realmente muito bem e é um monte de trabalho para replicar tudo ... vergonha ...
Roger
E esse link também não funciona. Para quem tentar encontrá-lo, observe que o URL correto requer um ponto-e-vírgula no final dele - o SO está causando o; para ser excluído dos links que as pessoas estão postando.
Roger
3
AFNetworking é o que a maioria das pessoas parece estar usando agora.
Vadoff
7

Eu pensei em atualizar este post um pouco e dizer que muita comunidade iOS mudou para a AFNetworking depois que ASIHTTPRequestfoi abandonada. Eu recomendo. É um ótimo invólucro NSURLConnectione permite chamadas assíncronas e basicamente qualquer coisa que você possa precisar.

Jesse Naugher
fonte
2
Eu sei que a resposta aceita é boa, não significa comportamento ou algo assim, mas isso definitivamente deve ter mais votos positivos. Talvez se um exemplo e algum fragmento de código forem adicionados, como a pergunta sugere?
acrespo 6/09/13
6

Aqui está uma resposta atualizada para iOS7 +. Ele usa NSURLSession, o novo hotness. Isenção de responsabilidade, este não foi testado e foi escrito em um 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]);
}

Ou melhor ainda, use o AFNetworking 2.0+. Normalmente, eu subclasse AFHTTPSessionManager, mas estou colocando tudo isso em um único método para ter um exemplo 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];
}

Se você estiver usando o serializador de resposta JSON, o responseObject será objeto da resposta JSON (geralmente NSDictionary ou NSArray).

Kyle Robson
fonte
1

NOTA: Exemplo do Pure Swift 3 (Xcode 8): Experimente o seguinte código de exemplo. É o exemplo simples de dataTaskfunção 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
fonte
0

Xcode 8 e 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()

Chamada de delegado do 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 o bloco 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()

Trabalhando bem para mim .. tente 100% garantia de resultado

Saumil Shah
fonte
0

Veja como a solicitação HTTP POST funciona para iOS 8 ou superior 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 isso satisfaça os seguintes requisitos.

Abilash Balasubramanian
fonte