Yêu cầu HTTP trong Swift với phương thức POST


188

Tôi đang cố chạy Yêu cầu HTTP trong Swift, để POST 2 tham số cho URL.

Thí dụ:

Liên kết: www.thisismylink.com/postName.php

Param:

id = 13
name = Jack

Cách đơn giản nhất để làm điều đó là gì?

Tôi thậm chí không muốn đọc phản hồi. Tôi chỉ muốn gửi nó để thực hiện các thay đổi trên cơ sở dữ liệu của mình thông qua tệp PHP.


Câu trả lời:


409

Trong Swift 3 trở lên, bạn có thể:

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

Ở đâu:

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

Điều này kiểm tra cả lỗi mạng cơ bản cũng như lỗi HTTP cấp cao. Điều này cũng đúng phần trăm thoát khỏi các tham số của truy vấn.

Lưu ý, tôi đã sử dụng một nametrong số Jack & Jill, để minh họa x-www-form-urlencodedkết quả chính xác name=Jack%20%26%20Jill, đó là mã hóa phần trăm được mã hóa phần trăm (nghĩa là không gian được thay thế bằng %20&giá trị được thay thế bằng %26).


Xem bản sửa đổi trước của câu trả lời này cho phiên bản Swift 2.


7
FYI, nếu bạn muốn thực hiện các yêu cầu thực tế (bao gồm phần trăm thoát, tạo các yêu cầu phức tạp, đơn giản hóa việc phân tích cú pháp các phản hồi), hãy xem xét sử dụng AlamoFire , từ tác giả của AFNetworking. Nhưng nếu bạn chỉ muốn thực hiện một POSTyêu cầu tầm thường , bạn có thể sử dụng ở trên.
Cướp

2
Cảm ơn Rob, đó chỉ là những gì tôi đang tìm kiếm! Không có gì hơn một POST đơn giản. Câu trả lời chính xác!
cuộc thi

1
Sau một vài giờ tìm kiếm một số giải pháp khác nhau, dòng 3 và 4 đang cứu mạng tôi vì tôi không thể để cuộc sống của mình khiến NSJSONSerialization.dataWithJSONObject hoạt động!
Zork

1
@complexi - Thay vì vẽ các kết nối giữa $_POSTvà tên tệp, tôi sẽ giảm điều này thành một điều đơn giản hơn: Tập lệnh PHP hoàn toàn không chạy nếu bạn không hiểu đúng về URL. Nhưng không phải lúc nào bạn cũng phải bao gồm tên tệp (ví dụ: máy chủ có thể thực hiện định tuyến URL hoặc có tên tệp mặc định). Trong trường hợp này, OP đã cung cấp cho chúng tôi một URL bao gồm tên tệp, vì vậy tôi chỉ đơn giản sử dụng cùng một URL như anh ấy đã làm.
Cướp

1
Alamofire không tốt hơn và không tệ hơn URLSessionvề vấn đề này. Tất cả các API mạng vốn đã không đồng bộ, cũng như vậy. Bây giờ, nếu bạn đang tìm kiếm các cách xử lý các yêu cầu không đồng bộ duyên dáng khác, bạn có thể xem xét gói chúng (hoặc URLSessioncác yêu cầu hoặc các yêu cầu của Alamofire) trong Operationlớp con tùy chỉnh, không đồng bộ . Hoặc bạn có thể sử dụng một số thư viện hứa hẹn, như PromiseKit.
Rob

69

Swift 4 trở lên

@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
Tôi nhận được lỗi sau với mã của bạn "Dữ liệu không thể đọc được vì nó không đúng định dạng."
applecrusher

Tôi nghĩ rằng bạn đang nhận được phản hồi ở định dạng Chuỗi, bạn có thể xác minh?
Suhit Patil

1
Tôi nghĩ vấn đề ở đây trong giải pháp này là bạn truyền tham số là json serialization và dịch vụ web đang lấy tham số formdata
Amr Angry

có trong giải pháp các thông số là json, vui lòng kiểm tra với máy chủ nếu nó yêu cầu dữ liệu biểu mẫu sau đó thay đổi loại nội dung, ví dụ request.setValue ("application / x-www-form-urlencoding", forHTTPHeaderField: "Kiểu nội dung")
Suhit Patil

đối với thông số nhiều phần, hãy sử dụng let ranhConstant = "--V2ymHFg03ehbqgZCaKO6jy--"; request.addvalue ( "multipart / form-data ranh giới = (boundaryConstant)", forHTTPHeaderField: "Content-Type")
Suhit Patil

17

Đối với bất cứ ai đang tìm kiếm một cách rõ ràng để mã hóa yêu cầu POST trong Swift 5.

Bạn không cần phải đối phó với việc thêm phần trăm mã hóa theo cách thủ công. Sử dụng URLComponentsđể tạo URL yêu cầu NHẬN. Sau đó, sử dụng querythuộc tính của URL đó để có được chuỗi truy vấn thoát phần trăm đúng.

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

Đây querysẽ là một chuỗi thoát đúng:

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

Bây giờ bạn có thể tạo một yêu cầu và sử dụng truy vấn dưới dạng HTTPBody:

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

Bây giờ bạn có thể gửi yêu cầu.


Sau nhiều ví dụ khác nhau, chỉ điều này hoạt động cho Swift 5.
Oleksandr

Tôi đã yêu cầu GET nhưng tôi tự hỏi làm thế nào về yêu cầu POST? Làm cách nào để truyền tham số vào httpBody hoặc tôi cần nó?
Mertalp Tasdelen

Giải pháp thông minh! Cảm ơn đã chia sẻ @pointum. Tôi chắc chắn Martalp không cần câu trả lời nữa, nhưng đối với bất kỳ ai khác đọc, ở trên có yêu cầu POST.
Vlad Spreys

12

Đây là phương pháp tôi đã sử dụng trong thư viện đăng nhập của mình: https://github.com/goktugyil/QorumLogs

Phương pháp này điền vào các biểu mẫu html bên trong 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
những gì đang application/x-www-form-urlencodedđược bạn thiết lập gì?
Mật ong

Để truyền dữ liệu trong cơ thể yêu cầu @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
Điều này có thể cần cập nhật cho Swift 3/4 để sử dụng URLRequest
Adam Ware

2

Tất cả các câu trả lời ở đây đều sử dụng các đối tượng JSON. Điều này đã cho chúng tôi các vấn đề với các $this->input->post() phương thức của bộ điều khiển Codeigniter. Không CI_Controllerthể đọc JSON trực tiếp. Chúng tôi đã sử dụng phương pháp này để làm điều đó mà KHÔNG CÓ 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()

Bây giờ CI_Controll của bạn sẽ có thể nhận param1param2sử dụng $this->input->post('param1')$this->input->post('param2')

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.