如何在Swift中执行GET和POST请求?

5
我将从 Ray Wenderlich 的 iOS Apprentice 教程第 4 部分中改编此代码。
该代码作为 GET 请求发送到我的 Strongloop API,使用简单的数据库模型。但是:
1. 这个代码可以工作,但我不知道它为什么能工作,因为我看不到任何实际发送请求的方法。 2. 我没有看到任何将其转换为 POST 请求的方法。
我的问题是:如何执行 POST 请求?是否需要完全不同的方式?
如果您需要更多信息,请告诉我。
    class ViewController: UIViewController {


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func getFromDB() {
        let url = urlWithSearchText("")
        println("URL: '\(url)'")
        if let jsonString = performGetRequestWithURL(url) {
            println("Received JSON string '\(jsonString)'")
        }
    }


    func urlWithSearchText(searchText: String) -> NSURL {
        let escapedSearchText = searchText.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
        let urlString = String(format: "http://localhost:3000/api/Tests", escapedSearchText)
        let url = NSURL(string: urlString)
        return url!
    }    

    func performGetRequestWithURL(url: NSURL) -> String? {
        var error: NSError?
        if let resultString = String(contentsOfURL: url, encoding: NSUTF8StringEncoding, error: &error) {
            return resultString
        } else if let error = error {
            println("Download Error: \(error)")
        } else {
            println("Unknown Download Error")
        }
        return nil
    }

这是实现效果的图片: https://dl.dropboxusercontent.com/u/14464971/Images/Messages%20Image%281477993527%29.png
8个回答

7

Swift 3及以上版本

GET请求

 func getRequest() {
    
    // request url
    let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! // change the url
    
    // create URLSession with default configuration
    let session = URLSession.shared
    
    // create dataTask using the session object to send data to the server
    let task = session.dataTask(with: url) { data, response, error in
        
        if let error = error {
            print("GET Request Error: \(error.localizedDescription)")
            return
        }
        
        // ensure there is valid response code returned from this HTTP response
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
                  print("Invalid Response received from the server")
                  return
              }
        
        // ensure there is data returned
        guard let responseData = data else {
            print("nil Data received from the server")
            return
        }
        
        do {
            // serialise the data object into Dictionary [String : Any]
            if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
                print(jsonResponse)
            } else {
                print("data maybe corrupted or in wrong format")
                throw URLError(.badServerResponse)
            }
        } catch let error {
            print("JSON Parsing Error: \(error.localizedDescription)")
        }
    }
    // resume the task
    task.resume()
}

POST

func postRequest() {
    
    // declare the parameter as a dictionary that contains string as key and value combination. considering inputs are valid
    
    let parameters: [String: Any] = ["name": "abc", "password": "password@123"]
    
    // create the url with URL
    let url = URL(string: "http://myServerName.com/api")! //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
    
    // add headers for the request
    request.addValue("application/json", forHTTPHeaderField: "Content-Type") // change as per server requirements
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    
    do {
        // convert parameters to Data and assign dictionary to httpBody of request
        request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
    } catch let error {
        print(error.localizedDescription)
        return
    }
    
    // create dataTask using the session object to send data to the server
    let task = session.dataTask(with: request) { data, response, error in
        
        if let error = error {
            print("Post Request Error: \(error.localizedDescription)")
            return
        }
        
        // ensure there is valid response code returned from this HTTP response
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode)
        else {
            print("Invalid Response received from the server")
            return
        }
        
        // ensure there is data returned
        guard let responseData = data else {
            print("nil Data received from the server")
            return
        }
        
        do {
            // create json object from data or use JSONDecoder to convert to Model stuct
            if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
                print(jsonResponse)
                // handle json response
            } else {
                print("data maybe corrupted or in wrong format")
                throw URLError(.badServerResponse)
            }
        } catch let error {
            print(error.localizedDescription)
        }
    }
    task.resume()
}

谢谢分享Suhit!我强烈推荐将其作为一个通用函数,传递url和参数。然后可以设置处理程序,以便开发人员在调用函数时处理异常/数据。我会更新我的回答,包含相同的内容。 - Peder Wessel

4
以下是两种POST方法。根据您是否需要同步(其他内容等待POST方法完成)或异步(POST方法在后台运行,其他方法并行运行)来选择。 方法
// POST data to url
func postDataAsynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ()) {
    var URL: NSURL = NSURL(string: url)!
    var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
    request.HTTPMethod = "POST";
    request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){

        response, data, error in

        var output: String!

        if data != nil {
            output = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
        }

        completionHandler(responseString: output, error: error)
    }
}

// Obtain the data
func postDataSynchronous(url: String, bodyData: String, completionHandler: (responseString: String!, error: NSError!) -> ())
{
    let URL: NSURL = NSURL(string: url)!
    var request:NSMutableURLRequest = NSMutableURLRequest(URL:URL)
    request.HTTPMethod = "POST"
    request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding);
    request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

    var response: NSURLResponse?
    var error: NSError?

    // Send data
    let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)

    var output: String! // Default to nil

    if data != nil{
        output =  NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
    }

    completionHandler(responseString: output, error: error)

}

如何使用

您可以像这样调用(使用)它们:

postDataSynchronous(url, bodyData: bodyData) {
    responseString, error in

        if error != nil {
            println("Error during post: \(error)")
            return
        }
        else{
            //Success
            println(responseString)
            userType = responseString // Set usertype based on server response
        }        
    }

SWIFT 2.0

func postData(url: String, params: Dictionary<String, String>, completionHandler: (data: NSData?, response: NSURLResponse?, error: NSError?) -> ()) {

    // Indicate download
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true

    let url = NSURL(string: url)!
    //        print("URL: \(url)")
    let request = NSMutableURLRequest(URL: url)
    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    // Verify downloading data is allowed
    do {
        request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: [])
    } catch let error as NSError {
        print("Error in request post: \(error)")
        request.HTTPBody = nil
    } catch {
        print("Catch all error: \(error)")
    }

    // Post the data
    let task = session.dataTaskWithRequest(request) { data, response, error in
        completionHandler(data: data, response: response, error: error)

        // Stop download indication
        UIApplication.sharedApplication().networkActivityIndicatorVisible = false // Stop download indication

    }

    task.resume()

}

这非常有帮助,但是假设bodyData是{"name": "Dumbledore教授","type": "Wizard"},我该如何将其转换为字符串?它里面有很多引号,但我不确定如何将其格式化为字符串。 - Nathan McKaskle
刚才那条评论请忽略,我已经解决了:“name=Professor Dumbledore&Type=Wizard”。 - Nathan McKaskle
很高兴看到你解决了问题 - 祝你编程愉快,周末愉快! - Peder Wessel
@FelipeCaldas 请参见上面的答案。 - Peder Wessel

2
guard let url = URL(string: "https://jsonplaceholder.typicode.com/users") else { return }

    let session = URLSession.shared
    session.dataTask(with: url) { (data, response, error) in
        if let response = response {
            print(response)
        }

        if let data = data {
            print(data)
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: [])
                print(json)
            } catch {
                print(error)
            }

        }
    }.resume()
}

这是一个GET方法。


1
该方法调用HTTP请求。
String(contentsOfURL: url, encoding: NSUTF8StringEncoding, error: &error) 

因为Swift的String没有像这样的init签名。
该方法将在项目的某个地方编写,作为Stringextension 它会是这样的
extension String{
    init(contentsOfURL: NSURL, encoding: NSUTF8StringEncoding, inout error: NSError){
        // load data from url
        self = //parse data to string
    }
}

我没有为String编写任何扩展。那是内置于SDK中的吗? - Nathan McKaskle
有没有办法用这个来发送一个POST请求呢? - Nathan McKaskle
1
你可以做到。请查看此链接:https://dev59.com/-V8d5IYBdhLWcg3w21Om - nRewik

1

GET请求

 func getRequest(with url: URL, callback: @escaping (Any?) -> Swift.Void) -> Void {

    let defaultConfigObject = URLSessionConfiguration.default
    defaultConfigObject.timeoutIntervalForRequest = 30.0
    defaultConfigObject.timeoutIntervalForResource = 60.0

    let session = URLSession.init(configuration: defaultConfigObject, delegate: nil, delegateQueue: nil)

    var urlRequest = URLRequest(url: url as URL)
    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")

    urlRequest.httpMethod = "GET"
    session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in

        guard    let httpResponse: HTTPURLResponse = response as? HTTPURLResponse
            else {
                print("Error: did not receive data")
                return
              }

        var response : (Any)? = nil

        if httpResponse.statusCode == 200 {
            print(httpResponse)

            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }

            do {
                let responseData = try JSONSerialization.jsonObject(with: responseData, options: [JSONSerialization.ReadingOptions.allowFragments])

                  response = responseData
                 callback(response)
            }
            catch _ as NSError {

                let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
                callback(responseString)
                return
            }
        }
        else {
            print(httpResponse)

            guard error == nil else {
                print("error calling GET on /todos/1")
                print(error ?? "error")
                callback(response!)
                return
            }

        }
    }).resume()
}

POST请求
//MARK: post request
func postRequest(with url:URL, postBody:String, callback: @escaping (Any?) -> Void) -> Void {

    let defaultConfigObject = URLSessionConfiguration.default
    defaultConfigObject.timeoutIntervalForRequest = 30.0
    defaultConfigObject.timeoutIntervalForResource = 60.0

    let session = URLSession.init(configuration: defaultConfigObject, delegate: nil, delegateQueue: nil)

    let params: String! = postBody

    var urlRequest = URLRequest(url: url as URL)
    urlRequest.httpMethod = "POST"

    let data = params.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
    urlRequest.httpBody = data

    session.dataTask(with: urlRequest, completionHandler: { (data, urlResponse, error) in


        guard let httpResponse:HTTPURLResponse = urlResponse as? HTTPURLResponse
            else{
                print("did not get any data")
                return
            }
        var response : (Any)? = nil

        if httpResponse.statusCode == 200 {

            guard let responseData = data else {
                print("Error: did not receive data")
                return
            }

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

                response = responseData
                callback(response)
            } catch _ as NSError {

                let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
                callback(responseString)
                return
            }
        }

        else {
        guard error == nil else {
            print("error calling GET on /todos/1")
            print(error ?? "error")
            callback(nil)
            return
        }
    }
    }).resume()
}

始终尝试检查HTTPURLResponse代码


1

String(contentsOfUrl:encoding:error)初始化程序在底层进行GET请求,并返回指定编码的内容字符串。

创建一个NSURLConnection并使用NSMutablrURLRequest设置HTTP方法post是一种发出请求的方式。使用NSMutableURLRequest,您可以创建一个NSURLConnection并立即使用委托启动它,或者您可以调用NSURLConnection.sendSynchronousRequest或NSURLConnection.sendAsynchronousRequest发送请求。


1
let parameters = ["username": "@Bipin_kumar", "tweet": "HelloWorld"]

guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, 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()

这是一个POST方法。

0

步骤:#1:APIRequest.swift

//
//  APIRequest.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

enum HTTPMethod: String {
    case GET
    case POST
    case PUT
    case DELETE
}

struct APIRequest {
    let url: URL
    let method: HTTPMethod
    let headers: [String: String]?
    let queryParams: [String: Any]?
    let body: Data?
}

步骤:#2:URLSessionApiClient.swift

//
//  URLSessionApiClient.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

final class URLSessionApiClient: NetworkClient {
    
    private let configuration: URLSessionConfiguration
    private let session: URLSession
    
    init() {
        self.configuration = URLSessionConfiguration.default
        self.configuration.timeoutIntervalForRequest = 30.0
        self.configuration.httpAdditionalHeaders = ["Content-Type": "application/json"]
        
        self.session = URLSession(configuration: self.configuration)
    }
    
    private func prepareURL(_ api: APIRequest) -> URL? {
        var urlComponents = URLComponents(string: api.url.absoluteString)
        let queryItems = api.queryParams?.map({ (key, value) in
            return URLQueryItem(name: key, value: String(describing: value) )
        })
        urlComponents?.queryItems = queryItems
        return urlComponents?.url
    }
    
    func dataTask<T: Codable>(_ api: APIRequest, onCompletion: @escaping (_ result: Result<T, Error>) -> Void) {
        
        guard let url = prepareURL(api) else {
            return onCompletion(.failure(AppError.invalidURL))
        }
        
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = api.method.rawValue
        urlRequest.allHTTPHeaderFields = api.headers
        urlRequest.httpBody = api.body
        
        self.session.dataTask(with: urlRequest) { data, response, error in
            
            // onFailure
            if let err = error {
                onCompletion(.failure(err))
                return
            }
            
            // Validation
            guard (200...299).contains((response as? HTTPURLResponse)?.statusCode ?? 0) else {
                onCompletion(.failure(AppError.httpFailure))
                return
            }
            
            // onSuccess
            if let data = data {
                // Transform Data to Codable Type
                if let userModel = try? JSONDecoder().decode(T.self, from: data) {
                    onCompletion(.success(userModel))
                } else {
                    onCompletion(.failure(AppError.decodingError))
                }
            } else {
                onCompletion(.failure(AppError.noHttpBody))
            }
        }.resume()
    }
}

步骤:#3:AppError.swift

//
//  AppError.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

enum AppError: Error {
    case invalidURL
    case noHttpBody
    case httpFailure
    case decodingError
    case error(Error)
    case custom(String)
}

步骤:#4:NetworkClient.swift

//
//  NetworkClient.swift
//  Demo
//
//  Created by MANNAM on 14/02/23.
//

import Foundation

protocol NetworkClient {
    func dataTask<T: Codable>(_ api: APIRequest,
                              onCompletion: @escaping (_ result: Result<T, Error>) -> Void)
}

步骤:#5:ViewController.Swift

var client: NetworkClient = URLSessionApiClient()
 
 func getRequest() {
     let postsAPI = URL(string: "https://url/posts")!
     let apiRequest = APIRequest(url: postsAPI, method: .GET, headers: nil, queryParams: nil, body: nil)
     
     client.dataTask(apiRequest) { (_ result: Result<PostModel, Error>) in
         switch result {
         case .failure(let error):
             print(error)
         case .success(let data):
             print("Data: \(data)")
         }
     }
 }
 
 func postRequest() {
     let newPost = PostModel(userId: 1234, id: 1234, title: "My Title", body: "This is Body")
     let newPostData = try? JSONEncoder().encode(newPost)
     
     let postsAPI = URL(string: "https://url/posts")!
     let apiRequest = APIRequest(url: postsAPI, method: .POST, headers: ["Content-Type":"application/json"], queryParams: nil, body: newPostData)
     client.dataTask(apiRequest) { (_ result: Result<PostModel, Error>) in
         switch result {
         case .failure(let error):
             print(error)
         case .success(let data):
             print("Data: \(data)")
         }
     }
 }

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