iOS: jak wykonać żądanie HTTP POST?

127

Zbliżam się do rozwoju iOS i chciałbym mieć jedną z moich pierwszych aplikacji do wykonywania żądania HTTP POST.

O ile rozumiem, powinienem zarządzać połączeniem, które obsługuje żądanie za pośrednictwem NSURLConnectionobiektu, co zmusza mnie do posiadania obiektu delegata, który z kolei będzie obsługiwał zdarzenia danych.

Czy ktoś mógłby wyjaśnić zadanie na praktycznym przykładzie?

Powinienem skontaktować się z punktem końcowym https wysyłającym dane uwierzytelniające (nazwa użytkownika i hasło) i otrzymującym odpowiedź w postaci zwykłego tekstu.

Federico Zancan
źródło

Odpowiedzi:

166

Możesz użyć NSURLConnection w następujący sposób:

  1. Ustaw NSURLRequest: Służy requestWithURL:(NSURL *)theURLdo inicjowania żądania.

    Jeśli musisz określić żądanie POST i / lub nagłówki HTTP, użyj NSMutableURLRequestz

    • (void)setHTTPMethod:(NSString *)method
    • (void)setHTTPBody:(NSData *)data
    • (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field
  2. Wyślij zapytanie na 2 sposoby NSURLConnection:

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

      Zwraca NSDatazmienną, którą można przetworzyć.

      WAŻNE: pamiętaj, aby rozpocząć synchroniczne żądanie w osobnym wątku, aby uniknąć blokowania interfejsu użytkownika.

    • Asynchronicznie: (void)start

Nie zapomnij ustawić delegata NSURLConnection do obsługi połączenia w następujący sposób:

- (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
źródło
4
Apple twierdzi, że używanie żądań synchronicznych jest „niezalecane” developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/… chociaż jeśli wiesz wystarczająco dużo, aby bawić się różnymi wątkami, prawdopodobnie wszystko będzie dobrze.
Aaron Brown
@Anh Ładna odpowiedź, ale byłem trochę sceptyczny co do ostatniej metody didReceiveAuthenticationChallenge. Czy są jakieś problemy z zabezpieczeniami związane z zakodowanymi na stałe hasłami / nazwami użytkowników? Czy można to obejść?
Sam Spencer
2
Zwykle można przechowywać poświadczenia w pęku kluczy i pobierać je tam, aby obsłużyć Basic-Auth.
Anh Do
2
iOS 5 i nowsze mogą również używać + (void) sendAsynchronousRequest: (NSURLRequest ) kolejka żądań: (NSOperationQueue *) queue completeHandler: (void (^) (NSURLResponse , NSData *, NSError *)) handler
chunkyguy
12

EDYCJA: ASIHTTPRequest został porzucony przez programistę. To wciąż naprawdę dobre IMO, ale prawdopodobnie powinieneś teraz poszukać gdzie indziej.

Gorąco polecam korzystanie z biblioteki ASIHTTPRequest, jeśli obsługujesz HTTPS. Nawet bez https zapewnia naprawdę fajne opakowanie dla takich rzeczy i chociaż nie jest trudno zrobić to samemu za pomocą zwykłego http, po prostu myślę, że biblioteka jest fajna i świetny sposób na rozpoczęcie.

Komplikacje HTTPS nie są trywialne w różnych scenariuszach, a jeśli chcesz być solidny w obsłudze wszystkich odmian, biblioteka ASI będzie naprawdę pomocna.

zrozumiałem
źródło
13
Biblioteka ASIHTTPRequest została oficjalnie porzucona przez jej programistę, zgodnie z tym postem: allseeing-i.com/[request_release] ; , Poleciłbym skorzystać z innych bibliotek zgodnie z sugestiami dewelopera, a nawet lepiej, spróbować nauczyć się NSURLRequest :) Pozdrawiam.
Goles
@ Mr.Gando - Twój link nie działa - zwróć uwagę, że średnik jest znaczący. To powiedziawszy, BARDZO smutne, że został porzucony. Naprawdę ładnie wykonuje wiele rzeczy uwierzytelniających i jest dużo pracy, aby to wszystko powtórzyć ... szkoda ...
Roger,
Ten link też nie działa. Dla każdego, kto próbuje go znaleźć, pamiętaj, że poprawny adres URL wymaga średnika na końcu - SO powoduje; być wykluczonym z linków publikowanych przez ludzi.
Roger,
3
Większość ludzi wydaje się teraz używać AFNetworking .
Vadoff
7

Pomyślałem, że zaktualizuję nieco ten post i powiem, że większość społeczności iOS przeniosła się do AFNetworking po tym, jak ASIHTTPRequestzostała porzucona. Gorąco polecam. Jest to świetne opakowanie NSURLConnectioni umożliwia wywołania asynchroniczne i w zasadzie wszystko, czego możesz potrzebować.

Jesse Naugher
źródło
2
Wiem, że przyjęta odpowiedź jest dobra, nie ma na myśli żadnej postawy ani nic, ale to zdecydowanie powinno mieć więcej pozytywnych głosów. Może jeśli dodamy przykład i fragment kodu, jak sugeruje pytanie?
acrespo
6

Oto zaktualizowana odpowiedź na iOS7 +. Używa NSURLSession, nowej popularności. Zastrzeżenie, to nie zostało przetestowane i zostało napisane w polu tekstowym:

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

Lub jeszcze lepiej, użyj AFNetworking 2.0+. Zwykle podklasę tworzyłbym AFHTTPSessionManager, ale umieszczam to wszystko w jednej metodzie, aby uzyskać zwięzły przykład.

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

Jeśli używasz serializatora odpowiedzi JSON, responseObject będzie obiektem z odpowiedzi JSON (często NSDictionary lub NSArray).

Kyle Robson
źródło
1

UWAGA: Przykład Pure Swift 3 (Xcode 8): Wypróbuj następujący przykładowy kod. To jest prosty przykład dataTaskfunkcji 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
źródło
0

Xcode 8 i Swift 3.0

Korzystanie z sesji URL:

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

Połączenie 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){

}

Korzystanie z funkcji 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()

U mnie działa dobrze… spróbuj 100% gwarancji wyniku

Saumil Shah
źródło
0

Oto jak działa żądanie POST HTTP dla iOS 8+ przy użyciu 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];

}

Mam nadzieję, że spełni to następujące wymagania.

Abilash Balasubramanian
źródło