HTTP-запрос в Swift с методом POST


190

Я пытаюсь запустить HTTP-запрос в Swift, чтобы параметры POST 2 к URL.

Пример:

Ссылка на сайт: www.thisismylink.com/postName.php

Params:

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
К вашему сведению, если вы хотите выполнять реальные запросы (включая процентное экранирование, создание сложных запросов, упрощение анализа ответов), рассмотрите возможность использования AlamoFire от автора AFNetworking. Но если вы просто хотите сделать тривиальный POSTзапрос, вы можете использовать выше.
Роб

2
Спасибо, Роб, это было именно то, что я искал! Ничего больше, чем простой пост. Отличный ответ!
гнев

1
После нескольких часов поиска нескольких различных решений строки 3 и 4 спасли мою жизнь, так как я не смог на всю жизнь заставить NSJSONSerialization.dataWithJSONObject работать!
Zork

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

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

71

Swift 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, вы можете проверить?
Сухит Патил

1
я думаю, что проблема в этом решении заключается в том, что вы передаете параметр как сериализацию json, а веб-сервис принимает параметры формданных
Amr Angry

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

для составных параметров используйте let borderConstant = "--V2ymHFg03ehbqgZCaKO6jy--"; request.addvalue ("multipart / form-data border = (borderConstant)", forHTTPHeaderField: "Content-Type")
Сухит Патил

19

Для тех, кто ищет чистый способ кодирования запроса 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Будет правильно спасся строка:

ключ1 = NeedToEscape% 3DAnd% 26 & ключ2 = v% C3% A5L% C3% до н.э.% C3% A9

Теперь вы можете создать запрос и использовать запрос как HTTPBody:

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

Теперь вы можете отправить запрос.


1
После нескольких примеров, только это работает для 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

Для передачи данных в теле запроса @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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.