如何在Swift中使用JSON体进行HTTP Post请求?

184

我正在尝试使用JSON数据体进行HTTP POST请求:

如何将NSDictionary添加到HTTP请求正文中。

这是我的代码,它似乎不能正常工作。

var entry1 = Response(IdQuestion: 6510,IdProposition: 10,Time: 30)
var entry2 = Response(IdQuestion: 8284,IdProposition: 10,Time: 30)
Responses.append(entry1)
Responses.append(entry2)

let list = Responses.map { $0.asDictionary }

let json = ["List":list,"IdSurvey":"102","IdUser":"iOSclient","UserInformation":"iOSClient"]

let data: NSData = NSKeyedArchiver.archivedDataWithRootObject(json)

NSJSONSerialization.isValidJSONObject(json)

let myURL = NSURL(string: "http://www.myserver.com")!
let request = NSMutableURLRequest(URL: myURL)
request.HTTPMethod = "POST"

request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")

request.HTTPBody = data
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
    data, response, error in
    println(response)
    // Your completion handler code here
}
task.resume()
18个回答

295

试试这个,

// prepare json data
let json: [String: Any] = ["title": "ABC",
                           "dict": ["1":"First", "2":"Second"]]

let jsonData = try? JSONSerialization.data(withJSONObject: json)

// create post request
let url = URL(string: "http://httpbin.org/post")!
var request = URLRequest(url: url)
request.httpMethod = "POST"

// insert json data to the request
request.httpBody = jsonData

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    guard let data = data, error == nil else {
        print(error?.localizedDescription ?? "No data")
        return
    }
    let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
    if let responseJSON = responseJSON as? [String: Any] {
        print(responseJSON)
    }
}

task.resume()

或者尝试一种方便的方式 Alamofire


2
非常漂亮。使用最新的Swift语法进行了更新(似乎每周都在变化)。 - Fattie
1
@JoeBlow - 请将您的替代方案发布为新答案。它与此答案有着根本性的不同,不能作为此答案的编辑。 - ChrisF
6
@JoeBlow - 我不确定社区对此是否有共识,因此我已经撤销了编辑,并在Meta.SO上询问了这个问题:http://meta.stackoverflow.com/questions/339024/are-edits-that-insert-swift-3-code-into-existing-swift-2-answers-acceptable - Brad Larson
6
对我而言,加入以下内容有所帮助: request.setValue("\(jsonData.length)", forHTTPHeaderField: "Content-Length") request.setValue("application/json", forHTTPHeaderField: "Content-Type") 参考 - Mohammad Zaid Pathan
10
除非您正确设置content-type头,否则这很危险:request.addValue("application/json", forHTTPHeaderField: "Content-Type")。否则,它会对表单执行该类型操作。超常见的ExpressJS + bodyParser设置将仅在某些情况下,根据内容而定得到错误的结果。因此,这会导致悄无声息的故障。 - sudo
显示剩余5条评论

66

Swift 4和5

在Swift 4中使用URLSession API进行HTTP POST请求

func postRequest(username: String, password: String, completion: @escaping ([String: Any]?, Error?) -> Void) {

    //declare parameter as a dictionary which contains string as key and value combination.
    let parameters = ["name": username, "password": password]

    //create the url with NSURL
    let url = URL(string: "https://www.myserver.com/api/login")!

    //create the session object
    let session = URLSession.shared

    //now create the Request 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 data object and set it as request body
    } catch let error {
        print(error.localizedDescription)
        completion(nil, error)
    }

    //HTTP Headers
    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, completionHandler: { data, response, error in

        guard error == nil else {
            completion(nil, error)
            return
        }

        guard let data = data else {
            completion(nil, NSError(domain: "dataNilError", code: -100001, userInfo: nil))
            return
        }

        do {
            //create json object from data
            guard let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] else {
                completion(nil, NSError(domain: "invalidJSONTypeError", code: -100009, userInfo: nil))
                return
            }
            print(json)
            completion(json, nil)
        } catch let error {
            print(error.localizedDescription)
            completion(nil, error)
        }
    })

    task.resume()
}

@objc func submitAction(_ sender: UIButton) {
    //call postRequest with username and password parameters
    postRequest(username: "username", password: "password") { (result, error) in
    if let result = result {
        print("success: \(result)")
    } else if let error = error {
        print("error: \(error.localizedDescription)")
    }
}

使用 Alamofire:

let parameters = ["name": "username", "password": "password123"]
Alamofire.request("https://www.myserver.com/api/login", method: .post, parameters: parameters, encoding: URLEncoding.httpBody)

2
一直在寻找“URLEncoding.httpBody”,谢谢。 - Tiago Mendes

49

使用Swift进行HTTP Post并捕获错误

let json = [ Activity.KEY_IDSUBJECT : activity.idSubject, Activity.KEY_RECORDMODE : "3", Activity.KEY_LOCATION_LONGITUDE : "0",Activity.KEY_LOCATION_LATITUDE : "0", Activity.KEY_CHECKIN : String(activity.dateCheckIn), Activity.KEY_CHECKOUT : String(activity.dateCheckOut) ]

do {
    let jsonData = try NSJSONSerialization.dataWithJSONObject(json, options: .PrettyPrinted)

    // create post request
    let url = NSURL(string: "https://...appspot.com/_ah/api/activityendpoint/v1/activity")!
    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "POST"

    // insert json data to the request
    request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
    request.HTTPBody = jsonData


    let task = NSURLSession.sharedSession().dataTaskWithRequest(request){ data, response, error in
        if error != nil{
            print("Error -> \(error)")
            return
        }

        do {
            let result = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String:AnyObject]

            print("Result -> \(result)")

        } catch {
            print("Error -> \(error)")
        }
    }

    task.resume()
    return task
} catch {
    print(error)
}

请求是否异步发送? - G.Abhisek
1
@Logan 文档中说,dataTaskWithRequest 提供“一个简单的可取消异步接口来接收数据”。因此,我会认为这个调用已经是异步的,不需要使用 dispatch_async。 - David Schumann

26

Swift 5的答案:

let json: [String: Any] = ["key": "value"]

let jsonData = try? JSONSerialization.data(withJSONObject: json)

// create post request
let url = URL(string: "http://localhost:1337/postrequest/addData")! //PUT Your URL
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("\(String(describing: jsonData?.count))", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// insert json data to the request
request.httpBody = jsonData

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    guard let data = data, error == nil else {
        print(error?.localizedDescription ?? "No data")
        return
    }
    let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
    if let responseJSON = responseJSON as? [String: Any] {
        print(responseJSON) //Code after Successfull POST Request
    }
}

task.resume()

3
正确的答案。这种复制粘贴的方式对我有效。 - podcastfan88
这适用于Swift 5.7吗? - thejdah

14

以下是Swift 5 Playground的代码示例,展示了使用JSONSerializationURLSession解决问题的一种可能方式:

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let url = URL(string: "http://localhost:8080/new")!
let jsonDict = ["firstName": "Jane", "lastName": "Doe"]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonDict, options: [])

var request = URLRequest(url: url)
request.httpMethod = "post"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
    if let error = error {
        print("error:", error)
        return
    }

    do {
        guard let data = data else { return }
        guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject] else { return }
        print("json:", json)
    } catch {
        print("error:", error)
    }
}

task.resume()

指定“application/json”作为ContentType非常重要,否则请求将被执行为普通的HTTP表单。好答案。 - heximal

5
let url = URL(string: "url")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")

request.httpMethod = "POST"



let postString = "ChangeAccordingtoyourdata=\(paramOne)&ChangeAccordingtoyourdata2=\(paramTwo)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
    guard let data = data, error == nil else {                                                 // check for fundamental networking error
        print("error=\(error)")
        return
    }

    if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {           // check for http errors
        print("statusCode should be 200, but is \(httpStatus.statusCode)")
        print("response = \(response)")

        SVProgressHUD.showError(withStatus: "Request has not submitted successfully.\nPlease try after some time")
    }

    let responseString = String(data: data, encoding: .utf8)
    print("responseString = \(responseString)")

    SVProgressHUD.showSuccess(withStatus: "Request has submitted successfully.\nPlease wait for a while")
    DispatchQueue.main.async {



    // enter code

    }

}
task.resume()

5

最新更新至2019年的完美nRewik答案:

创建字典:

let dic = [
    "username":u,
    "password":p,
    "gems":g ]

按照以下方式组装它:
var jsonData:Data?
do {
    jsonData = try JSONSerialization.data(
      withJSONObject: dic,
      options: .prettyPrinted)
} catch {
    print(error.localizedDescription)
}

按照以下方式创建请求,注意它是“post”方式。
let url = URL(string: "https://blah.com/server/dudes/decide/this")!
var request = URLRequest(url: url)

request.setValue("application/json; charset=utf-8",
     forHTTPHeaderField: "Content-Type")
request.setValue("application/json; charset=utf-8",
     forHTTPHeaderField: "Accept") 
request.httpMethod = "POST"
request.httpBody = jsonData

然后发送请求,检查是否出现网络错误(如无带宽等)或服务器返回的错误响应:

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    guard let data = data, error == nil else {
        // check for fundamental networking error
        print("fundamental networking error=\(error)")
        return
    }

    if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
        // check for http errors
        print("statusCode should be 200, but is \(httpStatus.statusCode)")
        print("response = \(response)")
    }

    let responseString = String(data: data, encoding: .utf8)
    print("responseString = \(responseString)")

幸运的是,现在这非常容易。

3

Swift 5.5

从 Swift 5.5 开始,我们现在有另一种使用async/await的替代方法:

// Form the POST request:
let url = URL(string: "http://example.com/login")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")

// Use the async variant of URLSession to make an HTTP POST request:
let (data, response) = try await URLSession.shared.upload(for: request, from: requestData)

完整示例:

struct LoginResponse: Decodable {
    let id: String
    let name: String
}

func login(_ username: String, _ password: String) async throws -> LoginResponse {
    struct RequestData: Encodable {
        let username: String
        let password: String
    }

    // Encode data to JSON to send in the POST request body:
    let encoder = JSONEncoder()
    let requestData = try encoder.encode(RequestData(username: username, password: password))

    // Form the POST request:
    let url = URL(string: "http://example.com/login")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")

    // Use the async variant of URLSession to make an HTTP POST request:
    let (data, response) = try await URLSession.shared.upload(for: request, from: requestData)

    print("HTTPURLResponse:", response)
    print("The response body is:", String(decoding: data, as: UTF8.self))

    // Parse the JSON response:
    return try JSONDecoder().decode(LoginResponse.self, from: data)
}

使用方法

您必须在异步上下文中调用它(这样您就可以await它)。一种方法是使用Task

Task {
    do {
        let loginResponse = try await login("user1", "mypassword")
        print("Login successful for user with id:", loginResponse.id)
    } catch {
        print(error)
    }
}

3
您可以像这样做:

您可以像以下示例一样进行操作:

func HTTPPostJSON(url: String,  data: NSData,
    callback: (String, String?) -> Void) {

        var request = NSMutableURLRequest(URL: NSURL(string: url)!)
        request.HTTPMethod = "POST"
        request.addValue("application/json",forHTTPHeaderField: "Content-Type")
        request.addValue("application/json",forHTTPHeaderField: "Accept")
        request.HTTPBody = data
        HTTPsendRequest(request, callback: callback)
}

func HTTPsendRequest(request: NSMutableURLRequest,
    callback: (String, String?) -> Void) {
        let task = NSURLSession.sharedSession()
            .dataTaskWithRequest(request) {
                (data, response, error) -> Void in
                if (error != nil) {
                    callback("", error.localizedDescription)
                } else {
                    callback(NSString(data: data,
                        encoding: NSUTF8StringEncoding)! as String, nil)
                }
        }

        task.resume()
}
//use
var data :Dictionary<String, AnyObject> = yourDictionaryData<--
var requestNSData:NSData = NSJSONSerialization.dataWithJSONObject(request, options:NSJSONWritingOptions(0), error: &err)!
HTTPPostJSON("http://yourPosturl..", data: requestNSData) { (response, error) -> Void in
    if error != nil{
        //error
        return;
    }

    println(response);
}

2

Swift4 - 苹果解决方案 "POST" 和 "Codable"

使用 request.httpmethod = "Post"Codable 结构体 将数据上传到网站:

@see: 清单2 配置URL请求

let userlogin = User(username: username, password: password, deviceid:UIDevice.current.identifierForVendor!.uuidString)

    guard let uploadData = try? JSONEncoder().encode(userlogin) else {
        print("Error UploadData: ")
        return
    }

    let urlUser = URL(string: APPURL.apiURL)!

    var request = URLRequest(url: urlUser)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    var responseStatus = 0

    let task = URLSession.shared.uploadTask(with: request, from: uploadData) { data, response, error in
        if let error = error {
            let code = (error as NSError).code
            print("Error:\(code) : \(error.localizedDescription)")
            completion(code)
            return
        }  
      guard let response = response as? HTTPURLResponse else {
            print("Invalid response")
            return
        }
// do your response handling here ...

这个答案并不是很好,但是那里的链接有一些最近可用的代码。 - Jonny
@Jonny:这只是最小的配置来获取POST,但你是对的,缺少可编码部分...我的错!我现在已经更新了这个。 - andreas-supersmart
可编码结构体是未来。 - Jonny
你好!我找到了这个答案,但无法用它解决我的问题,有人能在这里帮助我吗?:https://stackoverflow.com/questions/53598917/swift-4-how-to-pick-and-send-txt-file-via-post-request/53600049?noredirect=1#comment94067133_53600049 - biggreentree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接