iOS: comment effectuer une requête HTTP POST?

127

J'approche du développement iOS et j'aimerais avoir l'une de mes premières applications pour effectuer une requête HTTP POST.

Autant que je puisse comprendre, je devrais gérer la connexion qui gère la demande via un NSURLConnectionobjet, ce qui m'oblige à avoir un objet délégué, qui à son tour gérera les événements de données.

Quelqu'un pourrait-il clarifier la tâche avec un exemple pratique?

Je devrais contacter un point de terminaison https en envoyant des données d'authentification (nom d'utilisateur et mot de passe) et en obtenant une réponse en texte brut.

Federico Zancan
la source

Réponses:

166

Vous pouvez utiliser NSURLConnection comme suit:

  1. Définissez votre NSURLRequest: Utilisez requestWithURL:(NSURL *)theURLpour initialiser la demande.

    Si vous devez spécifier une requête POST et / ou des en-têtes HTTP, utilisez NSMutableURLRequestavec

    • (void)setHTTPMethod:(NSString *)method
    • (void)setHTTPBody:(NSData *)data
    • (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
  2. Envoyez votre demande de 2 manières en utilisant NSURLConnection:

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

      Cela renvoie une NSDatavariable que vous pouvez traiter.

      IMPORTANT: n'oubliez pas de lancer la demande synchrone dans un thread distinct pour éviter de bloquer l'interface utilisateur.

    • De manière asynchrone: (void)start

N'oubliez pas de définir le délégué de votre NSURLConnection pour gérer la connexion comme suit:

- (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
la source
4
Apple dit que l'utilisation de requêtes synchrones n'est "pas recommandée" developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ ... bien que si vous en savez assez pour jouer avec différents threads, tout ira probablement bien.
Aaron Brown
@Anh Nice Answer, mais j'étais un peu sceptique avec la dernière méthode didReceiveAuthenticationChallenge. Y a-t-il des problèmes de sécurité avec les mots de passe / noms d'utilisateur codés en dur? Y a-t-il un moyen de contourner cela?
Sam Spencer
2
En règle générale, vous stockez les informations d'identification dans le trousseau et les récupérez là pour gérer Basic-Auth.
Anh Do
2
iOS 5 et les versions ultérieures peuvent également utiliser + (void) sendAsynchronousRequest: (NSURLRequest ) request queue: (NSOperationQueue *) queue completionHandler: (void (^) (NSURLResponse , NSData *, NSError *)) handler
chunkyguy
13

EDIT: ASIHTTPRequest a été abandonné par le développeur. C'est toujours très bon IMO, mais vous devriez probablement chercher ailleurs maintenant.

Je recommande vivement d'utiliser la bibliothèque ASIHTTPRequest si vous gérez HTTPS. Même sans https, il fournit un très bon wrapper pour des trucs comme celui-ci et bien qu'il ne soit pas difficile de le faire soi-même sur http simple, je pense juste que la bibliothèque est agréable et constitue un excellent moyen de commencer.

Les complications HTTPS sont loin d'être anodines dans divers scénarios, et si vous voulez être robuste dans la gestion de toutes les variations, vous trouverez la bibliothèque ASI une véritable aide.

Roger
la source
13
La bibliothèque ASIHTTPRequest a été officiellement abandonnée par son développeur, comme l'indique cet article: allseeing-i.com/[request_release] ; , Je vous recommande d'utiliser d'autres bibliothèques comme le suggère le développeur, ou encore mieux, d'essayer d'apprendre NSURLRequest :) Cheers.
Goles
@ Mr.Gando - votre lien ne semble pas fonctionner - notez que le point-virgule est significatif. Cela dit, TRÈS triste de le voir abandonné. Il fait beaucoup de trucs d'authentification vraiment bien et c'est beaucoup de travail pour tout reproduire ... dommage ...
Roger
Et ce lien ne fonctionne pas non plus. Pour quiconque essaie de le trouver, veuillez noter que l'URL correcte nécessite un point-virgule à la fin - SO est à l'origine du; d'être exclu des liens que les gens publient.
Roger
3
AFNetworking est ce que la plupart des gens semblent utiliser actuellement.
Vadoff
7

Je pensais que je mettrais un peu à jour ce post et dire que beaucoup de la communauté iOS est passée à AFNetworking après avoir ASIHTTPRequestété abandonnée. Je le recommande fortement. C'est un excellent wrapper NSURLConnectionet permet les appels asynchrones, et essentiellement tout ce dont vous pourriez avoir besoin.

Jesse Naugher
la source
2
Je sais que la réponse acceptée est bonne, ne veut pas se comporter ou quoi que ce soit, mais cela devrait certainement avoir plus de votes positifs. Peut-être si un exemple et un fragment de code sont ajoutés, comme le suggère la question?
acrespo
6

Voici une réponse mise à jour pour iOS7 +. Il utilise NSURLSession, le nouveau hotness. Avertissement, ceci n'est pas testé et a été écrit dans un champ de texte:

- (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 mieux encore, utilisez AFNetworking 2.0+. Habituellement, je sous-classe AFHTTPSessionManager, mais je mets tout cela dans une seule méthode pour avoir un exemple concis.

- (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 vous utilisez le sérialiseur de réponse JSON, responseObject sera un objet de la réponse JSON (souvent NSDictionary ou NSArray).

Kyle Robson
la source
1

REMARQUE: exemple Pure Swift 3 (Xcode 8): essayez l'exemple de code suivant. C'est le simple exemple de dataTaskfonction 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
la source
0

Xcode 8 et Swift 3.0

Utilisation d'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()

Appel du délégué 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){

}

Utilisation du bloc 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()

Fonctionne bien pour moi .. essayez-le 100% de garantie de résultat

Saumil Shah
la source
0

Voici comment la requête HTTP POST fonctionne pour iOS 8+ à l'aide de 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];

}

J'espère que cela satisfera votre exigence suivante.

Abilash Balasubramanian
la source