iOS: Wie führe ich eine HTTP-POST-Anfrage durch?

128

Ich nähere mich der iOS-Entwicklung und möchte eine meiner ersten Anwendungen haben, die eine HTTP-POST-Anfrage ausführt.

Soweit ich verstehen kann, sollte ich die Verbindung, die die Anforderung verarbeitet, über ein NSURLConnectionObjekt verwalten, wodurch ich gezwungen bin, ein Delegatenobjekt zu haben, das wiederum Datenereignisse verarbeitet.

Könnte jemand bitte die Aufgabe mit einem praktischen Beispiel klären?

Ich sollte mich an einen https-Endpunkt wenden, der Authentifizierungsdaten (Benutzername und Kennwort) sendet und eine Klartextantwort zurückerhält.

Federico Zancan
quelle

Antworten:

167

Sie können NSURLConnection wie folgt verwenden:

  1. Stellen Sie Folgendes ein NSURLRequest: Verwenden Sie diese Option requestWithURL:(NSURL *)theURL, um die Anforderung zu initialisieren.

    Wenn Sie eine POST-Anforderung und / oder HTTP-Header angeben müssen, verwenden Sie NSMutableURLRequestmit

    • (void)setHTTPMethod:(NSString *)method
    • (void)setHTTPBody:(NSData *)data
    • (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
  2. Senden Sie Ihre Anfrage auf zwei Arten NSURLConnection:

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

      Dies gibt eine NSDataVariable zurück, die Sie verarbeiten können.

      WICHTIG: Denken Sie daran, die synchrone Anforderung in einem separaten Thread zu starten, um ein Blockieren der Benutzeroberfläche zu vermeiden.

    • Asynchron: (void)start

Vergessen Sie nicht, den Delegaten Ihrer NSURLConnection so einzustellen, dass die Verbindung wie folgt verarbeitet wird:

- (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.
quelle
4
Apple sagt, dass die Verwendung synchroner Anforderungen "nicht empfohlen" wird. Developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… obwohl Sie wahrscheinlich in Ordnung sind, wenn Sie genug wissen, um mit verschiedenen Threads herumzuspielen.
Aaron Brown
@Anh Schöne Antwort, aber ich war ein wenig skeptisch mit der letzten Methode didReceiveAuthenticationChallenge. Gibt es Sicherheitsprobleme mit fest codierten Passwörtern / Benutzernamen? Gibt es einen Weg daran vorbei?
Sam Spencer
2
Im Allgemeinen würden Sie die Anmeldeinformationen im Schlüsselbund speichern und dort abrufen, um Basic-Auth zu verarbeiten.
Anh Do
2
Ab iOS 5 kann auch + (void) sendAsynchronousRequest: (NSURLRequest ) Anforderungswarteschlange verwendet werden: (NSOperationQueue *) WarteschlangenabschlussHandler: (void (^) (NSURLResponse , NSData *, NSError *)) Handler
chunkyguy
13

BEARBEITEN: ASIHTTPRequest wurde vom Entwickler aufgegeben. Es ist immer noch sehr gut, IMO, aber Sie sollten jetzt wahrscheinlich woanders suchen.

Ich würde die Verwendung der ASIHTTPRequest-Bibliothek wärmstens empfehlen, wenn Sie mit HTTPS arbeiten. Selbst ohne https bietet es einen wirklich schönen Wrapper für solche Dinge, und obwohl es nicht schwer ist, sich über einfaches http hinwegzusetzen, finde ich die Bibliothek einfach nett und eine großartige Möglichkeit, um loszulegen.

Die HTTPS-Komplikationen sind in verschiedenen Szenarien alles andere als trivial. Wenn Sie alle Variationen robust handhaben möchten, ist die ASI-Bibliothek eine echte Hilfe.

Roger
quelle
13
Die ASIHTTPRequest-Bibliothek wurde von ihrem Entwickler offiziell aufgegeben, wie in diesem Beitrag angegeben: allseeing-i.com/[request_release] ; Ich würde Ihnen empfehlen, andere Bibliotheken zu verwenden, wie der Entwickler vorschlägt, oder noch besser, NSURLRequest zu lernen :) Prost.
Goles
@ Mr.Gando - Ihr Link scheint nicht zu funktionieren - beachten Sie, dass das Semikolon von Bedeutung ist. Das heißt, sehr traurig zu sehen, dass es aufgegeben wurde. Es macht viele Authentifizierungsaufgaben wirklich gut und es ist eine Menge Arbeit, alles zu replizieren ... schade ...
Roger
Und dieser Link funktioniert auch nicht. Wenn Sie versuchen, es zu finden, beachten Sie bitte, dass für die korrekte URL ein Semikolon am Ende erforderlich ist - SO verursacht das; von den Links ausgeschlossen zu werden, die Leute posten.
Roger
3
AFNetworking ist das, was die meisten Leute derzeit zu verwenden scheinen.
Vadoff
7

Ich dachte, ich würde diesen Beitrag ein wenig aktualisieren und sagen, dass ein Großteil der iOS-Community zu AFNetworking gewechselt ist, nachdem er ASIHTTPRequestaufgegeben wurde. Ich empfehle es sehr. Es ist ein großartiger Wrapper NSURLConnectionund ermöglicht asynchrone Anrufe und im Grunde alles, was Sie benötigen.

Jesse Naugher
quelle
2
Ich weiß, dass die akzeptierte Antwort gut ist, nicht bedeutet, sich zu benehmen oder so, aber dies sollte definitiv mehr positive Stimmen haben. Vielleicht, wenn ein Beispiel und ein Codefragment hinzugefügt werden, wie die Frage nahelegt?
Acrespo
6

Hier ist eine aktualisierte Antwort für iOS7 +. Es verwendet NSURLSession, die neue Schärfe. Haftungsausschluss, dies ist ungetestet und wurde in ein Textfeld geschrieben:

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

Oder noch besser, verwenden Sie AFNetworking 2.0+. Normalerweise würde ich AFHTTPSessionManager unterordnen, aber ich fasse dies alles in einer Methode zusammen, um ein kurzes Beispiel zu erhalten.

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

Wenn Sie den JSON-Antwortserialisierer verwenden, ist das responseObject ein Objekt aus der JSON-Antwort (häufig NSDictionary oder NSArray).

Kyle Robson
quelle
1

HINWEIS: Beispiel für Pure Swift 3 (Xcode 8): Probieren Sie den folgenden Beispielcode aus. Es ist das einfache Beispiel für die dataTaskFunktion von 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
quelle
0

Xcode 8 und Swift 3.0

Verwenden von 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()

URLSession Delegate-Aufruf:

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

}

Verwenden des Blocks 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()

Funktioniert gut für mich .. versuchen Sie es 100% Ergebnisgarantie

Saumil Shah
quelle
0

So funktioniert die POST-HTTP-Anforderung für iOS 8+ mit 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];

}

Hoffe, dass dies Ihre folgenden Anforderungen erfüllt.

Abilash Balasubramanian
quelle