Richiesta HTTP in Swift con metodo POST


190

Sto cercando di eseguire una richiesta HTTP in Swift, ai parametri POST 2 su un URL.

Esempio:

link: www.thisismylink.com/postName.php

Parametri:

id = 13
name = Jack

Qual è il modo più semplice per farlo?

Non voglio nemmeno leggere la risposta. Voglio solo inviarlo per eseguire modifiche sul mio database tramite un file PHP.


Risposte:


411

In Swift 3 e versioni successive puoi:

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

Dove:

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

Ciò verifica sia errori di rete fondamentali sia errori HTTP di alto livello. Anche questa percentuale in modo corretto sfugge ai parametri della query.

Nota, ho usato un namedi Jack & Jill, per illustrare il x-www-form-urlencodedrisultato corretto di name=Jack%20%26%20Jill, che è "codificato in percentuale" (ovvero lo spazio viene sostituito con %20e &il valore in viene sostituito con %26).


Vedi la revisione precedente di questa risposta per la rappresentazione di Swift 2.


7
Cordiali saluti, se vuoi fare richieste reali (tra cui la percentuale di escape, la creazione di richieste complesse, la semplificazione dell'analisi delle risposte), considera l'utilizzo di AlamoFire , dall'autore di AFNetworking. Ma se vuoi semplicemente fare una POSTrichiesta banale , puoi usare quanto sopra.
Rob,

2
Grazie Rob, era proprio quello che cercavo! Nient'altro che un semplice POST. Bella risposta!
angeant

1
Dopo alcune ore alla ricerca di diverse soluzioni, le linee 3 e 4 mi stanno salvando la vita perché non potevo per la vita di me far funzionare NSJSONSerialization.dataWithJSONObject!
Zork,

1
@complexi - Invece di tracciare connessioni tra $_POSTnomi di file e, ridurrei questo a qualcosa di più semplice: lo script PHP non funzionerà affatto se non ottieni l'URL corretto. Ma non è sempre necessario includere il nome file (ad esempio, il server potrebbe eseguire il routing URL o avere nomi file predefiniti). In questo caso, l'OP ci ha fornito un URL che includeva un nome file, quindi ho semplicemente usato lo stesso URL di lui.
Rob,

1
Alamofire non è migliore né peggiore di URLSessionquesto. Tutte le API di rete sono intrinsecamente asincrone, come dovrebbero essere. Ora, se stai cercando altri modi aggraziati per gestire le richieste asincrone, puoi prendere in considerazione l' URLSessionidea di raggrupparle ( richieste o Alamofire) in una Operationsottoclasse personalizzata asincrona . Oppure puoi usare alcune librerie di promesse, come PromiseKit.
Rob

71

Swift 4 e versioni successive

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

6
Viene visualizzato il seguente errore con il codice "Impossibile leggere i dati perché non sono nel formato corretto".
applecrusher,

Penso che stai ricevendo una risposta in formato String, puoi verificarlo?
Suhit Patil,

1
penso che il problema qui in questa soluzione sia che passi il parametro come serializzazione json e il servizio web sta prendendo come parametri formdata
Amr Angry,

sì in soluzione i parametri sono json, si prega di verificare con il server se richiede i dati del modulo, quindi modificare il tipo di contenuto, ad esempio request.setValue ("application / x-www-form-urlencoded", perHTTPHeaderField: "Content-Type")
Suhit Patil,

per i parametri multipart utilizzare let boundaryConstant = "--V2ymHFg03ehbqgZCaKO6jy--"; request.addvalue ("multipart / form-data boundary = (boundaryConstant)", forHTTPHeaderField: "Content-Type")
Suhit Patil,

19

Per chiunque cerchi un modo pulito per codificare una richiesta POST in Swift 5.

Non è necessario occuparsi dell'aggiunta manuale della codifica percentuale. Utilizzare URLComponentsper creare un URL di richiesta GET. Quindi utilizzare la queryproprietà di tale URL per ottenere la stringa di query con escape corretto in percentuale.

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

Il querysarà una stringa correttamente sfuggito:

key1 = NeedToEscape% 3DAnd% 26 & chiave2 = v% C3% A5L% C3% AC% C3% A9

Ora puoi creare una richiesta e utilizzare la query come HTTPBody:

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

Ora puoi inviare la richiesta.


1
Dopo vari esempi, solo questo funziona per Swift 5.
Oleksandr

Ho ricevuto la richiesta GET ma mi chiedo che ne pensi della richiesta POST? Come passare i parametri in httpBody o ne ho bisogno?
Mertalp Tasdelen,

Soluzione intelligente! Grazie per aver condiviso @pointum. Sono sicuro che Martalp non ha più bisogno della risposta, ma per chiunque legga, quanto sopra fa una richiesta POST.
Vlad Spreys,

12

Ecco il metodo che ho usato nella mia libreria di registrazione: https://github.com/goktugyil/QorumLogs

Questo metodo riempie i moduli HTML all'interno di Google Forms.

    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)

1
che application/x-www-form-urlencodedcosa stai impostando?
Miele

Per il trasferimento dei dati nel corpo della richiesta @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")
        }

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: test@test.com & 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()
}

1
Questo potrebbe richiedere l'aggiornamento per Swift 3/4 per utilizzare URLRequest
Adam Ware,

2

Tutte le risposte qui usano oggetti JSON. Questo ci ha dato problemi con i $this->input->post() metodi dei nostri controller Codeigniter. La CI_Controllernon può leggere direttamente JSON. Abbiamo usato questo metodo per farlo SENZA JSON

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

Ora il tuo CI_Controller sarà in grado di ottenere param1e param2utilizzare $this->input->post('param1')e$this->input->post('param2')

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.