HTTP-запит у Swift методом POST


189

Я намагаюся запустити HTTP-запит у Swift для встановлення параметрів POST 2 до URL-адреси.

Приклад:

Посилання: www.thisismylink.com/postName.php

Парами:

id = 13
name = Jack

Який найпростіший спосіб це зробити?

Я навіть не хочу читати відповідь. Я просто хочу надіслати це для зміни змін у моїй базі даних через файл PHP.


Відповіді:


411

У Swift 3 та пізніших версіях ви можете:

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

Де:

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

Це перевіряє як основні помилки в мережі, так і помилки HTTP високого рівня. Це також належним чином дозволяє уникнути параметрів запиту.

Зауважте, я використав nameдля Jack & Jill, щоб проілюструвати належний x-www-form-urlencodedрезультат name=Jack%20%26%20Jill, який є "відсотковим кодуванням" (тобто пробіл замінено %20і &значення замінено на %26).


Дивіться попередню редакцію цієї відповіді для випуску Swift 2.


7
FYI, якщо ви хочете робити реальні запити (включаючи втечу відсотків, створюючи складні запити, спростити розбір відповідей), подумайте про використання AlamoFire від автора AFNetworking. Але якщо ви просто хочете зробити банальний POSTзапит, ви можете скористатися вищесказаним.
Роб

2
Дякую Роб, саме це я шукав! Не що інше, як простий POST. Чудова відповідь!
ангел

1
Після кількох годин пошуку декількох різних рішень, рядки 3 і 4 врятують моє життя, оскільки я не міг за життя мене отримати NSJSONSerialization.dataWithJSONOоб'єкт працювати!
Зорк

1
@complexi - Замість того, щоб малювати з'єднання між $_POSTі іменами, я б зменшив це до чогось більш простого: сценарій PHP не працюватиме взагалі, якщо ви не отримаєте правильну URL-адресу. Але не завжди буває так, що потрібно вказати ім’я файлу (наприклад, сервер може робити маршрутизацію URL-адрес або мати назви файлів за замовчуванням). У цьому випадку ОП дав нам URL-адресу, яка включала ім'я файлу, тому я просто використовував ту саму URL-адресу, що і він.
Роб

1
Аламофір не кращий і не гірший, ніж URLSessionу цьому плані. Усі мережеві API по суті є асинхронними, як і вони повинні бути. Тепер, якщо ви шукаєте інші витончені способи поводження з асинхронними запитами, ви можете розглянути можливість їх загортання (або URLSessionзапити, або Alamofire) в асинхронний, Operationпідклас. Або ви можете використовувати якусь бібліотеку обіцянок, наприклад PromiseKit.
Роб

71

Швидкий рух 4 і вище

@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
Я отримую таку помилку з вашим кодом: "Дані не вдалося прочитати, оскільки вони не в правильному форматі".
applecrusher

Я думаю, що ви отримуєте відповідь у форматі String, чи можете ви перевірити?
Suhit Patil

1
Я думаю, що проблема в цьому рішенні полягає в тому, що ви передаєте параметр як серіализацію json, а веб-сервіс приймає параметри формату даних
Amr Angry

так, у рішенні параметри json, будь ласка, зверніться до сервера, чи потрібні дані форми, а потім змініть тип вмісту, наприклад, request.setValue ("application / x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
Suhit Патіль

для багаточастинних парамерів нехай використовує borderaryConstant = "--V2ymHFg03ehbqgZCaKO6jy--"; request.addvalue ("multipart / form-data border = = borderorderConstant)", forHTTPHeaderField: "Content-Type")
Suhit Patil

18

Для всіх, хто шукає чистий спосіб кодування запиту POST у Swift 5.

Вам не потрібно мати справу з вручну додавати відсоткове кодування. Використовуйте URLComponentsдля створення запиту GET URL. Потім використовуйте queryвластивість цієї URL-адреси, щоб отримати належним чином відсотковий рядок запиту.

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

Це queryбуде правильно виведений рядок:

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

Тепер ви можете створити запит і використовувати запит як HTTPBody:

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

Тепер ви можете надіслати запит.


Після різних прикладів, лише це працює для Swift 5.
Олександр

Я розмістив запит на отримання GET, але мені цікаво, як щодо запиту POST? Як передати параметри в httpBody чи мені це потрібно?
Мертальп Тасделен

Розумне рішення! Дякуємо, що поділилися @pointum. Я впевнений, що Martalp відповідь більше не потрібна, але для когось іншого, хто читає, вищезазначене запит POST.
Влад Шпрей

12

Ось метод, який я використав у своїй бібліотеці журналів: https://github.com/goktugyil/QorumLogs

Цей метод заповнює форми HTML у форматі 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
що application/x-www-form-urlencodedти встановлюєш?
Мед

Для передачі даних в орган запиту @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
Це може знадобитися оновлення для Swift 3/4, щоб використовувати URLRequest
Adam Ware

2

У всіх відповідях тут використовуються об'єкти JSON. Це $this->input->post() створило нам проблеми з методами наших контролерів Codeigniter. CI_ControllerНе може читати JSON безпосередньо. Ми використовували цей метод, щоб зробити це БЕЗ 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()

Тепер ваш CI_Controller зможе отримати param1та param2використовувати $this->input->post('param1')та$this->input->post('param2')

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.