HTTP-Anfrage in Swift mit POST-Methode

188

Ich versuche, eine HTTP-Anforderung in Swift auszuführen, um 2 Parameter an eine URL zu senden.

Beispiel:

Verknüpfung: www.thisismylink.com/postName.php

Parameter:

id = 13
name = Jack

Was ist der einfachste Weg, das zu tun?

Ich möchte nicht einmal die Antwort lesen. Ich möchte das nur senden, um Änderungen an meiner Datenbank über eine PHP-Datei vorzunehmen.

angeant
quelle

Antworten:

409

In Swift 3 und höher können Sie:

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

Wo:

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

Dies prüft sowohl auf grundlegende Netzwerkfehler als auch auf HTTP-Fehler auf hoher Ebene. Dieser Prozentsatz entgeht auch den Parametern der Abfrage.

Beachten Sie, dass ich ein namevon verwendet habe Jack & Jill, um das richtige x-www-form-urlencodedErgebnis von zu veranschaulichen name=Jack%20%26%20Jill, das "Prozent codiert" ist (dh das Leerzeichen wird durch ersetzt %20und das &im Wert wird durch ersetzt %26).


Siehe vorherige Überarbeitung dieser Antwort für die Swift 2-Wiedergabe.

rauben
quelle
7
Zu Ihrer Information: Wenn Sie echte Anforderungen ausführen möchten (einschließlich prozentualer Escape-Effekte , Erstellen komplexer Anforderungen, Vereinfachen des Parsens der Antworten), sollten Sie AlamoFire vom Autor von AFNetworking verwenden. Wenn Sie jedoch nur eine triviale POSTAnfrage stellen möchten , können Sie die oben genannten verwenden.
Rob
2
Danke Rob, das war genau das, wonach ich gesucht habe! Nichts weiter als ein einfacher POST. Gute Antwort!
Angeant
1
Nach ein paar Stunden der Suche nach verschiedenen Lösungen retten die Zeilen 3 und 4 mein Leben, da ich NSJSONSerialization.dataWithJSONObject nicht zum Laufen bringen konnte!
Zork
1
@complexi - Anstatt Verbindungen zwischen $_POSTund Dateinamen zu zeichnen , würde ich dies auf etwas Einfacheres reduzieren: Das PHP-Skript wird überhaupt nicht ausgeführt, wenn Sie die URL nicht korrekt erhalten. Es ist jedoch nicht immer der Fall, dass Sie den Dateinamen angeben müssen (z. B. führt der Server möglicherweise ein URL-Routing durch oder hat Standarddateinamen). In diesem Fall gab uns das OP eine URL mit einem Dateinamen, sodass ich einfach dieselbe URL wie er verwendete.
Rob
1
Alamofire ist nicht besser und nicht schlechter als URLSessionin dieser Hinsicht. Alle Netzwerk-APIs sind von Natur aus asynchron und sollten es auch sein. Wenn Sie nach anderen Möglichkeiten suchen, mit asynchronen Anforderungen umzugehen, können Sie diese (entweder URLSessionAnforderungen oder Alamofire-Anforderungen) in eine asynchrone, benutzerdefinierte OperationUnterklasse einschließen . Oder Sie können eine Versprechensbibliothek wie PromiseKit verwenden.
Rob
69

Swift 4 und höher

@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
quelle
6
Ich erhalte die folgende Fehlermeldung mit Ihrem Code "Die Daten konnten nicht gelesen werden, da sie nicht im richtigen Format vorliegen."
Applecrusher
Ich denke, Sie erhalten eine Antwort im String-Format. Können Sie das überprüfen?
Suhit Patil
1
Ich denke, das Problem hier in dieser Lösung ist, dass Sie den Parameter als JSON-Serialisierung übergeben und der Webdienst als Formdatenparameter verwendet
Amr Angry
Ja, in Lösung sind die Parameter json. Bitte erkundigen Sie sich beim Server, ob Formulardaten erforderlich sind, und ändern Sie dann den Inhaltstyp, z. B. request.setValue ("application / x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Suhit Patil
Verwenden Sie für mehrteilige Parameter let borderConstant = "--V2ymHFg03ehbqgZCaKO6jy--"; request.addvalue ("multipart / form-data border = (borderConstant)", forHTTPHeaderField: "Content-Type")
Suhit Patil
17

Für alle, die nach einer sauberen Möglichkeit suchen, eine POST-Anfrage in Swift 5 zu codieren.

Sie müssen sich nicht mit dem manuellen Hinzufügen einer prozentualen Codierung befassen. Verwenden Sie URLComponentsdiese Option , um eine GET-Anforderungs-URL zu erstellen. Verwenden Sie dann die queryEigenschaft dieser URL, um die prozentuale Escape-Abfragezeichenfolge zu erhalten.

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

Das querywird ein richtig maskierter String sein:

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

Jetzt können Sie eine Anfrage erstellen und die Abfrage als HTTPBody verwenden:

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

Jetzt können Sie die Anfrage senden.

pointum
quelle
Nach verschiedenen Beispielen funktioniert nur dies für Swift 5.
Oleksandr
Ich habe die GET-Anfrage geändert, aber ich frage mich, wie wäre es mit der POST-Anfrage? Wie übergebe ich Parameter an den httpBody oder brauche ich sie?
Mertalp Tasdelen
Schlaue Lösung! Vielen Dank für das Teilen von @pointum. Ich bin sicher, Martalp braucht die Antwort nicht mehr, aber für alle anderen Leser führt das oben Gesagte eine POST-Anfrage durch.
Vlad Spreys
12

Hier ist die Methode, die ich in meiner Protokollierungsbibliothek verwendet habe: https://github.com/goktugyil/QorumLogs

Diese Methode füllt HTML-Formulare in Google Forms aus.

    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
quelle
1
Was application/x-www-form-urlencodedstellst du ein?
Honey
Für die Übergabe von Daten im Anfragetext @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
quelle
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
quelle
1
Dieser muss möglicherweise aktualisiert werden, damit Swift 3/4 URLRequest
Adam Ware am
2

Alle Antworten hier verwenden JSON-Objekte. Dies gab uns Probleme mit den $this->input->post() Methoden unserer Codeigniter-Steuerungen. Der CI_Controllerkann JSON nicht direkt lesen. Wir haben diese Methode verwendet, um es OHNE JSON zu tun

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

Jetzt wird Ihre CI_Controller Lage zu bekommen , param1und param2mit $this->input->post('param1')und$this->input->post('param2')

CanonicalBear
quelle