Żądanie HTTP w Swift z metodą POST

189

Próbuję uruchomić żądanie HTTP w Swift, aby POST 2 parametry do adresu URL.

Przykład:

Połączyć: www.thisismylink.com/postName.php

Params:

id = 13
name = Jack

Jaki jest najprostszy sposób to zrobić?

Nie chcę nawet czytać odpowiedzi. Chcę tylko to wysłać, aby wykonać zmiany w mojej bazie danych za pomocą pliku PHP.

angeant
źródło

Odpowiedzi:

411

W Swift 3 i nowszych możesz:

let url = URL(string: "http://www.thisismylink.com/postName.php")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let parameters: [String: Any] = [
    "id": 13,
    "name": "Jack & Jill"
]
request.httpBody = parameters.percentEncoded()

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    guard let data = data, 
        let response = response as? HTTPURLResponse, 
        error == nil else {                                              // check for fundamental networking error
        print("error", error ?? "Unknown error")
        return
    }

    guard (200 ... 299) ~= response.statusCode else {                    // check for http errors
        print("statusCode should be 2xx, but is \(response.statusCode)")
        print("response = \(response)")
        return
    }

    let responseString = String(data: data, encoding: .utf8)
    print("responseString = \(responseString)")
}

task.resume()

Gdzie:

extension Dictionary {
    func percentEncoded() -> Data? {
        return map { key, value in
            let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
            return escapedKey + "=" + escapedValue
        }
        .joined(separator: "&")
        .data(using: .utf8)
    }
}

extension CharacterSet { 
    static let urlQueryValueAllowed: CharacterSet = {
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = "!$&'()*+,;="

        var allowed = CharacterSet.urlQueryAllowed
        allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
        return allowed
    }()
}

Sprawdza to zarówno podstawowe błędy sieci, jak i błędy HTTP wysokiego poziomu. To również poprawnie procent ucieka parametrom zapytania.

Zauważyć, że stosuje się nameod Jack & Jill, aby zilustrować prawidłowego x-www-form-urlencodedwyniku name=Jack%20%26%20Jill, który jest „procent zakodowany” (to znaczy przestrzeń jest zastąpiony %20i &wartości otrzymuje się %26).


Zobacz poprzednią wersję tej odpowiedzi dotyczącą wersji Swift 2.

Obrabować
źródło
7
Do Twojej wiadomości, jeśli chcesz wykonywać prawdziwe żądania (w tym procentowe zmiany znaczenia, tworzenie złożonych żądań, uprościć analizę odpowiedzi), rozważ użycie AlamoFire od autora AFNetworking. Ale jeśli chcesz po prostu zrobić trywialną POSTprośbę, możesz skorzystać z powyższego.
Rob
2
Dzięki, Rob, właśnie tego szukałem! Nic więcej niż prosty POST. Świetna odpowiedź!
angeant
1
Po kilku godzinach szukania kilku różnych rozwiązań, linie 3 i 4 ratują mi życie, ponieważ nie mogłem, aby moje życie mogło uzyskać NSJSONSerialization.dataWithJSONObject do pracy!
Zork
1
@complexi - Zamiast rysować połączenia między $_POSTi nazwy plików, zredukowałbym to do czegoś prostszego: skrypt PHP nie uruchomi się wcale, jeśli nie uzyskasz poprawnego adresu URL. Ale nie zawsze trzeba podać nazwę pliku (np. Serwer może wykonywać routing adresów URL lub mieć domyślne nazwy plików). W tym przypadku OP podał nam adres URL zawierający nazwę pliku, więc po prostu użyłem tego samego adresu URL co on.
Rob
1
Alamofire nie jest ani lepszy, ani gorszy niż URLSessionpod tym względem. Wszystkie sieciowe interfejsy API są z natury asynchroniczne, tak jak powinny. Teraz, jeśli szukasz innych wdzięcznych sposobów radzenia sobie z żądaniami asynchronicznymi, możesz rozważyć ich zawinięcie ( URLSessionżądania lub Alamofire) w asynchroniczną, niestandardową Operationpodklasę. Możesz też użyć biblioteki obietnic, na przykład PromiseKit.
Rob
71

Swift 4 i wyżej

@IBAction func submitAction(sender: UIButton) {

    //declare parameter as a dictionary which contains string as key and value combination. considering inputs are valid

    let parameters = ["id": 13, "name": "jack"]

    //create the url with URL
    let url = URL(string: "www.thisismylink.com/postName.php")! //change the url

    //create the session object
    let session = URLSession.shared

    //now create the URLRequest object using the url object
    var request = URLRequest(url: url)
    request.httpMethod = "POST" //set http method as POST

    do {
        request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
    } catch let error {
        print(error.localizedDescription)
    }

    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    //create dataTask using the session object to send data to the server
    let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in

        guard error == nil else {
            return
        }

        guard let data = data else {
            return
        }

        do {
            //create json object from data
            if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
                print(json)
                // handle json...
            }
        } catch let error {
            print(error.localizedDescription)
        }
    })
    task.resume()
}
Suhit Patil
źródło
6
W kodzie pojawia się następujący błąd: „Nie można odczytać danych, ponieważ nie są one w odpowiednim formacie”.
applecrusher
Myślę, że otrzymujesz odpowiedź w formacie ciągu. Czy możesz to zweryfikować?
Suhit Patil
1
myślę, że problemem w tym rozwiązaniu jest to, że przekazujesz parametr w postaci serializacji json, a usługa internetowa przyjmuje parametry formdata
Amr Angry
tak w rozwiązaniu parametry są json, proszę sprawdzić na serwerze, czy wymaga danych formularza, a następnie zmienić typ zawartości, np. request.setValue („application / x-www-form-urlencoded”, forHTTPHeaderField: „Content-Type”)
Suhit Patil,
dla parametrów wieloczęściowych użyj let granicaConstant = "--V2ymHFg03ehbqgZCaKO6jy--"; request.addvalue („multipart / form-data boundary = (boundaryConstant)”, forHTTPHeaderField: „Content-Type”)
Suhit Patil
18

Dla każdego, kto szuka czystego sposobu na zakodowanie żądania POST w Swift 5.

Nie musisz zajmować się ręcznym dodawaniem kodowania procentowego. Służy URLComponentsdo tworzenia adresu URL żądania GET. Następnie użyj querywłaściwości tego adresu URL, aby uzyskać poprawnie procentowy znak zapytania.

let url = URL(string: "https://example.com")!
var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!

components.queryItems = [
    URLQueryItem(name: "key1", value: "NeedToEscape=And&"),
    URLQueryItem(name: "key2", value: "vålüé")
]

let query = components.url!.query

queryBędzie właściwie uciekł ciąg:

key1 = NeedToEscape% 3DAnd% 26 i key2 = v% C3% A5l% C3% BC% C3% A9

Teraz możesz utworzyć żądanie i użyć zapytania jako HTTPBody:

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = Data(query.utf8)

Teraz możesz wysłać zapytanie.

punkt
źródło
Po różnych przykładach działa to tylko w Swift 5.
Oleksandr
Zamieszkałem żądanie GET, ale zastanawiam się, co powiesz na żądanie POST? Jak przekazać parametry do httpBody lub czy go potrzebuję?
Mertalp Tasdelen
Sprytne rozwiązanie! Dziękujemy za udostępnienie @pointum. Jestem pewien, że Martalp nie potrzebuje już odpowiedzi, ale dla każdego innego czytającego powyższe żądanie POST.
Vlad Spreys
12

Oto metoda, której użyłem w mojej bibliotece logowania: https://github.com/goktugyil/QorumLogs

Ta metoda wypełnia formularze HTML w Formularzach Google.

    var url = NSURL(string: urlstring)

    var request = NSMutableURLRequest(URL: url!)
    request.HTTPMethod = "POST"
    request.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
    request.HTTPBody = postData.dataUsingEncoding(NSUTF8StringEncoding)
    var connection = NSURLConnection(request: request, delegate: nil, startImmediately: true)
Esqarrouth
źródło
1
co application/x-www-form-urlencodedustawiasz
Honey
Za przekazanie danych w treści żądania @Honey
Achraf
4
let session = URLSession.shared
        let url = "http://...."
        let request = NSMutableURLRequest(url: NSURL(string: url)! as URL)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        var params :[String: Any]?
        params = ["Some_ID" : "111", "REQUEST" : "SOME_API_NAME"]
        do{
            request.httpBody = try JSONSerialization.data(withJSONObject: params, options: JSONSerialization.WritingOptions())
            let task = session.dataTask(with: request as URLRequest as URLRequest, completionHandler: {(data, response, error) in
                if let response = response {
                    let nsHTTPResponse = response as! HTTPURLResponse
                    let statusCode = nsHTTPResponse.statusCode
                    print ("status code = \(statusCode)")
                }
                if let error = error {
                    print ("\(error)")
                }
                if let data = data {
                    do{
                        let jsonResponse = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions())
                        print ("data = \(jsonResponse)")
                    }catch _ {
                        print ("OOps not good JSON formatted response")
                    }
                }
            })
            task.resume()
        }catch _ {
            print ("Oops something happened buddy")
        }
Osman
źródło
3
@IBAction func btn_LogIn(sender: AnyObject) {

    let request = NSMutableURLRequest(URL: NSURL(string: "http://demo.hackerkernel.com/ios_api/login.php")!)
    request.HTTPMethod = "POST"
    let postString = "email: [email protected] & password: testtest"
    request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request){data, response, error in
        guard error == nil && data != nil else{
            print("error")
            return
        }
        if let httpStatus = response as? NSHTTPURLResponse where httpStatus.statusCode != 200{
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(response)")
        }
        let responseString = String(data: data!, encoding: NSUTF8StringEncoding)
        print("responseString = \(responseString)")
    }
    task.resume()
}
Raish Khan
źródło
1
Ten może wymagać aktualizacji dla Swift 3/4, aby korzystać z URLRequest
Adam Ware
2

Wszystkie odpowiedzi tutaj wykorzystują obiekty JSON. To dało nam problemy z $this->input->post() metodami naszych kontrolerów Codeigniter. Nie CI_Controllermożna odczytać JSON bezpośrednio. Użyliśmy tej metody, aby to zrobić BEZ JSON

fun postRequest(){
//Create url object
guard let url = URL(string: yourURL) else {return}

//Create the session object
let session = URLSession.shared

//Create the URLRequest object using the url object
var request = URLRequest(url: url)

//Set the request method. Important Do not set any other headers, like Content-Type
request.httpMethod = "POST" //set http method as POST

//Set parameters here. Replace with your own.
let postData = "param1_id=param1_value&param2_id=param2_value".data(using: .utf8)
request.httpBody = postData
}

//Create a task using the session object, to run and return completion handler
let webTask = session.dataTask(with: request, completionHandler: {data, response, error in
guard error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
guard let serverData = data else {
print("server data error")
return
}
do {
if let requestJson = try JSONSerialization.jsonObject(with: serverData, options: .mutableContainers) as? [String: Any]{
print("Response: \(requestJson)")
}
} catch let responseError {
print("Serialisation in error in creating response body: \(responseError.localizedDescription)")
let message = String(bytes: serverData, encoding: .ascii)
print(message as Any)
}
})
//Run the task
webTask.resume()

Teraz twój CI_Controller będzie mógł uzyskać param1i param2używać $this->input->post('param1')oraz$this->input->post('param2')

CanonicalBear
źródło