iOS: come eseguire una richiesta POST HTTP?


127

Mi sto avvicinando allo sviluppo di iOS e mi piacerebbe avere una delle mie prime applicazioni per eseguire una richiesta POST HTTP.

Per quanto posso capire, dovrei gestire la connessione che gestisce la richiesta tramite un NSURLConnectionoggetto, che mi costringe ad avere un oggetto delegato, che a sua volta gestirà gli eventi di dati.

Qualcuno potrebbe chiarire l'attività con un esempio pratico?

Dovrei contattare un endpoint https inviando dati di autenticazione (nome utente e password) e ottenendo una risposta in chiaro.

Risposte:


166

È possibile utilizzare NSURLConnection come segue:

  1. Imposta NSURLRequest: Usa requestWithURL:(NSURL *)theURLper inizializzare la richiesta.

    Se è necessario specificare una richiesta POST e / o intestazioni HTTP, utilizzare NSMutableURLRequestcon

    • (void)setHTTPMethod:(NSString *)method
    • (void)setHTTPBody:(NSData *)data
    • (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
  2. Invia la tua richiesta in 2 modi usando NSURLConnection:

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

      Ciò restituisce una NSDatavariabile che è possibile elaborare.

      IMPORTANTE: ricordati di dare il via alla richiesta sincrona in un thread separato per evitare di bloccare l'interfaccia utente.

    • in modo asincrono: (void)start

Non dimenticare di impostare il delegato di NSURLConnection per gestire la connessione come segue:

- (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];
}

4
Apple afferma che l'utilizzo di richieste sincrone non è "consigliato" developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… anche se se si conosce abbastanza per scherzare con thread diversi, probabilmente starai bene.
Aaron Brown,

@Anh Bella risposta, ma ero un po 'scettico con l'ultimo metodo didReceiveAuthenticationChallenge. Ci sono problemi di sicurezza con password / nomi utente codificati? C'è un modo per aggirare questo?
Sam Spencer,

2
Generalmente dovresti archiviare le credenziali nel portachiavi e recuperarle lì per gestire Basic-Auth.
Anh Do,

2
iOS 5 in poi può anche usare + (void) sendAsynchronousRequest: (NSURLRequest ) coda di richiesta: (NSOperationQueue *) completamento della coda Handler: (void (^) (NSURLResponse , NSData *, NSError *)) handler
chunkyguy

13

EDIT: ASIHTTPRequest è stato abbandonato dallo sviluppatore. È ancora veramente buono IMO, ma probabilmente dovresti cercare altrove ora.

Consiglio vivamente di utilizzare la libreria ASIHTTPRequest se stai gestendo HTTPS. Anche senza HTTPS fornisce un wrapper davvero bello per cose come questa e anche se non è difficile fare te stesso su un semplice http, penso solo che la libreria sia carina e un ottimo modo per iniziare.

Le complicazioni HTTPS sono tutt'altro che banali in vari scenari e se vuoi essere robusto nel gestire tutte le varianti, troverai la libreria ASI un vero aiuto.


13
La libreria ASIHTTPRequest è stata ufficialmente abbandonata dal suo sviluppatore come afferma questo post: allseeing-i.com/[request_release] ; , Ti consiglierei di usare altre librerie come suggerisce lo sviluppatore, o ancora meglio, provare a imparare NSURLRequest :) Saluti.
Goles,

@ Mr.Gando - il tuo link non sembra funzionare - nota che il punto e virgola è significativo. Detto questo, MOLTO triste vederlo abbandonato. Fa un sacco di roba di autenticazione davvero bene ed è un sacco di lavoro per replicare tutto ... peccato ...
Roger

E anche quel link non funziona. Per chiunque cerchi di trovarlo, ti preghiamo di notare che l'URL corretto richiede un punto e virgola alla fine di esso - SO sta causando il; per essere escluso dai link che le persone pubblicano.
Roger,

3
AFNetworking è ciò che la maggior parte delle persone sembra utilizzare ora.
Vadoff,

7

Ho pensato di aggiornare un po 'questo post e dire che molti membri della community iOS sono passati ad AFNetworking dopo che è ASIHTTPRequeststato abbandonato. Lo consiglio vivamente. È un ottimo wrapper NSURLConnectione consente chiamate asincrone e praticamente tutto ciò di cui potresti aver bisogno.


2
So che la risposta accettata è buona, non significa comportamento o altro, ma questo dovrebbe sicuramente avere più voti. Forse se viene aggiunto un esempio e un frammento di codice, come suggerisce la domanda?
acrespo,

6

Ecco una risposta aggiornata per iOS7 +. Utilizza NSURLSession, il nuovo hotness. Disclaimer, questo non è testato ed è stato scritto in un campo di testo:

- (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 meglio ancora, usa AFNetworking 2.0+. Di solito vorrei sottoclassare AFHTTPSessionManager, ma sto mettendo tutto in un metodo per avere un esempio 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 si utilizza il serializzatore di risposte JSON, responseObject sarà oggetto della risposta JSON (spesso NSDictionary o NSArray).


1

NOTA: esempio di Pure Swift 3 (Xcode 8): provare il seguente codice di esempio. È il semplice esempio di dataTaskfunzione di 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()

    }

0

Xcode 8 e Swift 3.0

Utilizzando 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()

Chiamata delegata 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){

}

Utilizzando Block 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()

Funzionando bene per me .. provalo garanzia del risultato al 100%


0

Ecco come funziona la richiesta HTTP POST per 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];

}

Spero che questo soddisfi i seguenti requisiti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.