如何在 Swift 中发出 NSURLSession POST 请求

Posted

技术标签:

【中文标题】如何在 Swift 中发出 NSURLSession POST 请求【英文标题】:How to make NSURLSession POST request in Swift 【发布时间】:2017-02-02 08:42:54 【问题描述】:

嗨,我是 Swift 的初学者,我正在尝试让 NSURLSession “发布”请求发送一些参数,如我下面的代码

根据我下面的代码响应不是来自服务器,请有人帮助我

BackGroundClass:-

 import UIKit

protocol sampleProtocal

    func getResponse(result:NSDictionary)
    func getErrorResponse(error:NSString)


class BackGroundClass: NSObject 

    var delegate:sampleProtocal?

    func callPostService(url:String,parameters:NSDictionary)


        print("url is===>\(url)")

        let request = NSMutableURLRequest(URL: NSURL(string:url)!)

        let session = NSURLSession.sharedSession()
        request.HTTPMethod = "POST"

        //Note : Add the corresponding "Content-Type" and "Accept" header. In this example I had used the application/json.
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")

        request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(parameters, options: [])

        let task = session.dataTaskWithRequest(request)  data, response, error in
            guard data != nil else 
                print("no data found: \(error)")
                return
            

            do 
                if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary 
                    print("Response: \(json)")
                    self.mainResponse(json)
                 else 
                    let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)// No error thrown, but not NSDictionary
                    print("Error could not parse JSON: \(jsonStr)")
                    self.eroorResponse(jsonStr!)
                
             catch let parseError 
                print(parseError)// Log the error thrown by `JSONObjectWithData`
                let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
                print("Error could not parse JSON: '\(jsonStr)'")
                self.eroorResponse(jsonStr!)
            
        

        task.resume()
    

    func mainResponse(result:NSDictionary)
        delegate?.getResponse(result)
    

    func eroorResponse(result:NSString)
        delegate?.getErrorResponse(result)
    

视图控制器:-

import UIKit

class ViewController: UIViewController,sampleProtocal 

    override func viewDidLoad() 
        super.viewDidLoad()

        let delegate = BackGroundClass();
        delegate.self;

        let params = ["scancode":"KK03799-008", "UserName":"admin"] as Dictionary<String, String>

        let backGround=BackGroundClass();
        backGround.callPostService("url", parameters: params)
    

    func getResponse(result: NSDictionary) 
        print("Final response is\(result)");
    

    func getErrorResponse(error: NSString) 
        print("Final Eroor code is\(error)")
    

【问题讨论】:

你遇到了什么错误.. 我没有收到错误,但没有收到响应,并且邮递员响应即将到来 您是否在应用中添加了传输安全性,请参阅***.com/questions/30731785/… 是的,我已经在我的 plist 文件中添加了传输安全 检查你的网址和参数是否正确发送 【参考方案1】:

Swift 4 使用 json 有效负载发布示例-

func postAction(_ sender: Any) 
    let Url = String(format: "your url")
    guard let serviceUrl = URL(string: Url) else  return 
    let parameterDictionary = ["username" : "Test", "password" : "123456"]
    var request = URLRequest(url: serviceUrl)
    request.httpMethod = "POST"
    request.setValue("Application/json", forHTTPHeaderField: "Content-Type")
    guard let httpBody = try? JSONSerialization.data(withJSONObject: parameterDictionary, options: []) else 
        return
    
    request.httpBody = httpBody
    
    let session = URLSession.shared
    session.dataTask(with: request)  (data, response, error) in
        if let response = response 
            print(response)
        
        if let data = data 
            do 
                let json = try JSONSerialization.jsonObject(with: data, options: [])
                print(json)
             catch 
                print(error)
            
        
    .resume()

【讨论】:

非常感谢朋友,一定要把这个作为正确答案! 如果只有 Apple 的文档会发布像这样完整的实际使用示例。 :/。谢谢。 请告诉我,如果我也将图像上传到后端?我无法发布带有数据的图像,我收到状态码 --> 400【参考方案2】:

尝试运行此函数并打印响应,它在 Swift 4.0 中。

在这里,我准备了可编码的结构:

struct LoginData: Codable 
    var code: Int?
    var message: String?
    var status: String?
    var token: String?
    var data: DataSet?

struct DataSet: Codable 
    var email : String?
    var contactNo : String?
    var firstName : String?
    var lastName: String?
    var dob : String?
    var gender : String?
    var address: String?
    var city : String?
    var state : String?
    var country : String?
    var zip : String?
    var username: String?

如果您的响应打印正确,则将其传递给您的 viewController。

func loginWS(parameters:[String:String], completionHandler: @escaping (Any?) -> Swift.Void) 

    guard let gitUrl = URL(string: BASE_URL+ACTION_URL) else  return 
    print(gitUrl)

    let request = NSMutableURLRequest(url: gitUrl)
    //  uncomment this and add auth token, if your project needs.
    //  let config = URLSessionConfiguration.default
    //  let authString = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMywiUGFzc3dvcmQiOiIkMmEkMTAkYVhpVm9wU3JSLjBPYmdMMUk2RU5zdU9LQzlFR0ZqNzEzay5ta1pDcENpMTI3MG1VLzR3SUsiLCJpYXQiOjE1MTczOTc5MjV9.JaSh3FvpAxFxbq8z_aZ_4OhrWO-ytBQNu6A-Fw4pZBY"
    //  config.httpAdditionalHeaders = ["Authorization" : authString]

    let session = URLSession.shared
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    request.httpBody = try! JSONSerialization.data(withJSONObject: parameters, options: [])

    let task = session.dataTask(with: request as URLRequest)  data, response, error in

        guard let data = data else  return 
        do 
        //  let decoder = JSONDecoder()
        //  here replace LoginData with your codable structure.
            let gitData = try JSONDecoder().decode(LoginData.self, from: data)
            print("response data:", gitData) 
            completionHandler(gitData)
         catch let err 
            print("Err", err)
        
        .resume()

【讨论】:

【参考方案3】:

这是一个兼容 Swift 4Swift 5 的完整解决方案示例。

Endpoint 创建网址

struct Endpoint 
    let path: String
    let queryItems: [URLQueryItem]?


extension Endpoint 
    var url: URL? 
        var components = URLComponents()
        components.scheme = "https"
        components.host = "YOUR_HOST"
        components.path = path
        components.queryItems = queryItems
        return components.url
    

User 请求正文的对象模型

struct User: Encodable 
    let name: String
    let surname: String
    let age: Int

    // this is to customise init 
    init(name: String,
         surname: String,
         age: Int) 
        self.name = name
        self.surname = surname
        self.age = age
    

    enum CodingKeys: String, CodingKey 
        case name, surname, age
    

UserResponse http响应模型来自API

struct UserResponse: Decodable 
    let message: String
    let userId: String?

    enum CodingKeys: String, CodingKey 
        case message, userId = "user_id" // API returns userId as "user_id"
    

APIClient 向我们的 api 发出 http 请求

protocol APIClientProtocol: Any 
    func sendUser(_ user: User, completionBlock: @escaping (_ userResponse: UserResponse?, _ error: APIClient.Error?) -> Void)


class APIClient: APIClientProtocol 

    fileprivate let defaultSession: URLSession = 
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 10.0
        configuration.timeoutIntervalForResource = 10.0
        return URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
    ()

    public init()  

    public func uploadUser(_ user: User, completionBlock: @escaping (UserResponse?, APIClient.Error?) -> Void) 
        guard let url = Endpoint(path: "/user/upload", queryItems: nil).url else 
            completionBlock(nil, .brokenURL)
            return
        
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"
        urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
        urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")
        do 
            let jsonData = try JSONEncoder().encode(user)
            urlRequest.httpBody = jsonData
         catch 
            completionBlock(nil, .serialization(error.localizedDescription))
            return
        
        let task = defaultSession.dataTask(with: urlRequest)  data, urlResponse, error in
            if let error = error 
                completionBlock(nil, .http(error.localizedDescription))
                return
            
            guard let httpResponse = urlResponse as? HTTPURLResponse else 
                return
            
            if httpResponse.statusCode == 200 
                guard let data = data else 
                    return
                
                do 
                    let userResponse = try JSONDecoder().decode(UserResponse.self, from: data)
                    completionBlock(userResponse, nil)
                 catch let error 
                    completionBlock(nil, .serialization(error.localizedDescription))
                
             else 
                completionBlock(nil, .http("Status failed!"))
            
        
        task.resume()
    



extension APIClient 

    enum Error: Swift.Error, Equatable 
        case brokenURL
        case serialization(String)
        case http(String)
    


【讨论】:

“Content-Type”和“Accept”在请求中都是强制性的吗?我只是想知道。我们不能只通过“接受”吗?【参考方案4】:

发布类

func post(params : Dictionary<String, String>, url : String) 
    var request = NSMutableURLRequest(URL: NSURL(string: url))
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    var err: NSError?
    request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    var task = session.dataTaskWithRequest(request, completionHandler: data, response, error -> Void in
        println("Response: \(response)")
        var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
        println("Body: \(strData)")
        var err: NSError?
        var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary

        // Did the JSONObjectWithData constructor return an error? If so, log the error to the console
        if(err != nil) 
            println(err!.localizedDescription)
            let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
            println("Error could not parse JSON: '\(jsonStr)'")
        
        else 
            // The JSONObjectWithData constructor didn't return an error. But, we should still
            // check and make sure that json has a value using optional binding.
            if let parseJSON = json 
                // Okay, the parsedJSON is here, let's get the value for 'success' out of it
                var success = parseJSON["success"] as? Int
                println("Succes: \(success)")
            
            else 
                // Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
                let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
                println("Error could not parse JSON: \(jsonStr)")
            
        
    )

    task.resume()

像这样调用这个方法

 self.post(["username":"jameson", "password":"password"], url: "http://localhost:4567/login")

希望对你有帮助:)

【讨论】:

谢谢。在我添加了两个 addValue 方法后,它开始为我工作!【参考方案5】:

Http 正文丢失。示例 - 将字符串参数设置为正文

let paramString = String(format:"param1=%@&param2=%@",param1,param2)
request.httpBody = paramString.data(using: String.Encoding.utf8)

这里试试

request.httpBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)

【讨论】:

我想设置字典而不是字符串 好的。它只是一个例子 将此值设置为http body NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)【参考方案6】:
func getData(searchString:String,completion:@escaping(Any)->Void)
    let url = "https://itunes.apple.com/search?term="+searchString
    URLSession.shared.dataTask(with: URL.init(string: url)!)(data,response,err) in
        if let responsedata = data
            DispatchQueue.main.async 
                completion(responsedata)
            
        
    .resume()

【讨论】:

考虑至少简要说明您在此处所做的工作,以及您认为导致原始问题的原因。非常感谢。【参考方案7】:

试试这个:(Swift 4.2)

public func submitDelivery(delivery:DeliveryModal,responseCode:String,completion:@escaping SubmitCompletionBlock)

    let urlString = BaseURL.getURL(urlType: .submit(responseCode))
    guard let url = URL(string: urlString) else  return 
    var request : URLRequest = URLRequest(url: url)
    request.httpMethod = HttpMethod.post.rawValue
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    let encoder = JSONEncoder()
    encoder.outputFormatting = .prettyPrinted

    do 
        let jsonData = try encoder.encode(delivery)
        request.httpBody = jsonData

     catch 
        print(error.localizedDescription)
        completion(nil,nil,NSError.init())
    

    let dataTask = URLSession.shared.dataTask(with: request) 
        data,response,error in

        guard let data = data else 
            completion(nil,response,NSError.init())
            return
        
        do 
            let data = try JSONDecoder().decode(DeliverySubmitResponseModal.self, from: data)
            DispatchQueue.main.async 
                completion(data,response,error)
            
         catch let error 
            debugPrint(error.localizedDescription)
        

    
    dataTask.resume()

【讨论】:

以上是关于如何在 Swift 中发出 NSURLSession POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Alamofire 请求 swift 3 上发出警报

是否可以理解 swift 3 中何时发出通知?

如何在 iOS 应用程序的后台发出间歇性 HTTP POST 请求 - Swift

在 Swift 上发出多个网络请求

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

使用 swift 4 发出 GET 请求在游乐场中有效,但在项目中无效