使用 POST 方法的 Swift 中的 HTTP 请求

Posted

技术标签:

【中文标题】使用 POST 方法的 Swift 中的 HTTP 请求【英文标题】:HTTP Request in Swift with POST method 【发布时间】:2014-12-09 11:44:29 【问题描述】:

我正在尝试在 Swift 中运行 HTTP 请求,将 2 个参数发布到 URL。

例子:

链接:www.thisismylink.com/postName.php

参数:

id = 13
name = Jack

最简单的方法是什么?

我什至不想阅读回复。我只是想发送它以通过 PHP 文件对我的数据库执行更改。

【问题讨论】:

***.com/a/48306950/6898523 【参考方案1】:

在 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 错误。这也正确地百分比转义了查询的参数。

注意,我使用Jack & Jill 中的name 来说明name=Jack%20%26%20Jill 的正确x-www-form-urlencoded 结果,即“百分比编码”(即空格替换为%20&中的值替换为%26)。


请参阅previous revision of this answer 了解 Swift 2 版本。

【讨论】:

仅供参考,如果您想做真正的请求(包括百分比转义、创建复杂请求、简化响应解析),请考虑使用 AFNetworking 的作者的AlamoFire。但是如果你只是想做一个琐碎的POST请求,你可以使用上面的。 谢谢 Rob,这正是我想要的!无非是一个简单的 POST。很好的答案! Alamofire 在这方面不比URLSession 好也不差。所有网络 API 本质上都是异步的,它们也应该如此。现在,如果您正在寻找其他优雅的方式来处理异步请求,您可以考虑将它们(URLSession 请求或 Alamofire 请求)包装在异步的自定义 Operation 子类中。或者你可以使用一些 Promise 库,比如 PromiseKit。 @DeepBlue - 我明白你在说什么,但我不同意。如果出现问题,默默地失败是一个非常的坏主意。也许你可以做guard let url = ... else fatalError("Invalid URL") ,但那是句法噪音,没有什么好处。您正在为一些不是最终用户运行时问题而是编程问题错误的事情编写大量错误处理代码。类比是隐式展开的@IBOutlet 引用。您是否为所有网点编写了大量guard let label = ... 代码?不,那太傻了。这里也一样。 别误会我的意思。如果有些事情不是立即显而易见的,或者由于程序员无法控制的原因(比如解析 JSON 响应和/或处理网络错误)而失败,那么使用强制展开运算符是一个巨大的错误。绝对安全地打开那些。但是对于像@IBOutlet 或这个 URL 示例这样的东西,添加语法噪音会适得其反,恕我直言。并且使用仅执行 returnelse 子句来创建 guard,隐藏任何潜在问题,这是一个非常糟糕的主意。【参考方案2】:

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

【讨论】:

您的代码出现以下错误“无法读取数据,因为它的格式不正确。” 我认为您收到的是字符串格式的回复,您可以验证一下吗? 我认为这个解决方案中的问题是您将参数作为 json 序列化传递,而 Web 服务将作为 formdata 参数 是的,解决方案中的参数是 json,如果需要表单数据,请与服务器确认,然后更改内容类型,例如request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") 这应该是正确的答案,因为谁想像检查的答案所暗示的那样在字符串中形成他们的数据...#oldSkool【参考方案3】:

适用于任何寻求在 Swift 5 中以简洁方式对 POST 请求进行编码的人。

您无需手动添加百分比编码。 使用 URLComponents 创建 GET 请求 URL。然后使用该 URL 的 query 属性来正确获取转义查询字符串的百分比。

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 请求。 顺便说一下,如果您使用这种技术,请注意它不会正确地对+ 字符进行百分比转义。见***.com/a/27724627/1271826。 完美运行,最后我刚刚添加了 URLSession.shared.dataTask(with: request) data, HTTPURLResponse, Error in if (data != nil && data?.count != 0) let response = String(data: data!, encoding: .utf8) print(response!) .resume()【参考方案4】:

这是我在日志库中使用的方法:https://github.com/goktugyil/QorumLogs

此方法填充 Google 表单中的 html 表单。

    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)

【讨论】:

application/x-www-form-urlencoded 是什么@你在设置什么? 用于在请求正文@Honey 中传递数据【参考方案5】:
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")
        

【讨论】:

【参考方案6】:

这里的所有答案都使用 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 将能够使用 $this->input->post('param1')$this->input->post('param2') 获取 param1param2

【讨论】:

【参考方案7】:
@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()

【讨论】:

这个可能需要更新 Swift 3/4 才能使用 URLRequest

以上是关于使用 POST 方法的 Swift 中的 HTTP 请求的主要内容,如果未能解决你的问题,请参考以下文章

Alamofire POST 方法中的 POST 对象数组 - Swift/IOS

Swift 3 - 创建简单请求方法 POST (URLRequestConvertible) 的问题

Swift4 url​​Session Decodable 中的 POST 方法不起作用(这里出了啥问题?)

Swift&NodeJS 使用Alamofire进行Post(zhuan)

在 Swift 中使用 JSON 正文发出 HTTP Post 请求

如何在 swift 中使用 NSURLSession POST JSON 数据