Perform POST request in iOS Swift

2019-02-11 04:25发布

问题:

I am trying perform a POST request and the request does not go through. I have looked through Perform POST request in Swift already but it does not contain what I'm looking for.

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
    var request = NSMutableURLRequest(URL: NSURL(string: "https://us1.lacunaexpanse.com"))
    println("request url https://us1.lacunaexpanse.com")
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    let apikey = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    println("apikey",apikey)

    let username = "username"
    let password = "password"

    var login = Array(["username", "password", "apikey"])

    let jsonDictionary = ["2.0", "jsonrpc", "1", "id", "login", "method", "login", "params"]
    println("jsonDictionary",jsonDictionary)

    var writeError: NSError?

    let jsonData = NSJSONSerialization.dataWithJSONObject(jsonDictionary, options: NSJSONWritingOptions(), error: NSErrorPointer())

    var resultAsString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)

    resultAsString = resultAsString.stringByAppendingString("empire")

    let url = NSURL.URLWithString("string")
    println("url",url)

    var request2 = NSMutableURLRequest()
    println("Post url =%@",url)

    request2 = NSMutableURLRequest(URL:url)

    request2.HTTPMethod = "POST"

    var connection = NSURLConnection(request: request, delegate: self, startImmediately: false)

    return true

回答1:

There are a whole bunch of tactical issues here:

  1. You're creating NSURLSession, but then issuing NSURLConnection request. Pick one or the other (you might as well use NSURLSession).

  2. Your "request dictionary" isn't a dictionary, but rather an array. For example, to issue the JSON-RPC request, the proper format of the dictionary is:

    let requestDictionary = [
        "jsonrpc" : "2.0",
        "id"      : 1,
        "method"  : "login",
        "params"  : ["myuserid", "mypassword", "mykey"]
    ]
    
  3. Minor issue, but you're using a lot of variables (via var) where a constant (via let) would be fine. In the spirit of Swift's safety, use let wherever possible.

  4. According to the Lacuna Expanse API, your URL should be including the module name.

    So, for example if doing POST requests in the "Empire" module, the URL is:

    let url = NSURL(string: "https://us1.lacunaexpanse.com/empire")
    
  5. You're likely to be doing a lot of requests, so I'd suggest putting the bulk of that in a single function that you can call again and again, without repeating code all over the place. Perhaps a function like the following that takes the following parameters:

    • module (e.g. "empire" vs "alliance");

    • method (e.g. "login" vs "fetch_captcha");

    • the parameters appropriate for that request (e.g. for "login", that would be the "name", "password", and the "api_key"); and

    • closure that will be called when the asynchronous request finishes.

    This function then prepares the JSON-RPC request and calls the closure when the request finishes:

    func submitLacunaRequestFromModule(module: String, method: String, parameters: AnyObject, completion: (AnyObject?, NSError?) -> ()) -> NSURLSessionTask? {
        let session = NSURLSession.sharedSession()
        let url = NSURL(string: "https://us1.lacunaexpanse.com")!.URLByAppendingPathComponent(module)
        let request = NSMutableURLRequest(URL: url)
        request.HTTPMethod = "POST"
        request.setValue("application/json-rpc", forHTTPHeaderField: "Content-Type")
    
        let requestDictionary = [
            "jsonrpc" : "2.0",
            "id"      : 1,
            "method"  : method,
            "params"  : parameters
        ]
    
        request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(requestDictionary, options: [])
    
        let task = session.dataTaskWithRequest(request) { data, response, error in
    
            // handle fundamental network errors (e.g. no connectivity)
    
            guard error == nil && data != nil else {
                completion(data, error)
                return
            }
    
            // check that http status code was 200
    
            if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode != 200 {
                completion(String(data: data!, encoding: NSUTF8StringEncoding), nil)
            }
    
            // parse the JSON response
    
            do {
                let responseObject = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary
                completion(responseObject, nil)
            } catch let error as NSError {
                completion(String(data: data!, encoding: NSUTF8StringEncoding), error)
            }
        }
        task.resume()
    
        return task
    }
    

    This does all of the necessary wrapping of the method and parameters within a JSON-RPC request. Then, all you need to do to call that method is something like so:

    submitLacunaRequestFromModule("empire", method: "login", parameters: ["myuserid", "mypassword", "mykey"]) { responseObject, error in
    
        // some network error or programming error
    
        guard error == nil else {
            print("error = \(error)")
            print("responseObject = \(responseObject)")
            return
        }
    
        // network request ok, now see if login was successful
    
        if let responseDictionary = responseObject as? NSDictionary {
            if let errorDictionary = responseDictionary["error"] as? NSDictionary {
                print("error logging in (bad userid/password?): \(errorDictionary)")
            } else if let resultDictionary = responseDictionary["result"] as? NSDictionary {
                print("successfully logged in, refer to resultDictionary for details: \(resultDictionary)")
            } else {
                print("we should never get here")
                print("responseObject = \(responseObject)")
            }
        }
    }
    

    For a request that requires a dictionary, such as "create", just go ahead and supply the dictionary:

    submitLacunaRequestFromModule("empire", method: "create", parameters: [
        "name"      : "user",
        "password"  : "password",
        "password1" : "password",
        "captcha_guid" : "305...dd-....-....-....-e3706...73c0",
        "captcha_solution" : "42",
        "email" : "test@gmail.com"
        ]) { responseObject, error in
    
            guard error == nil else {
                print("error = \(error)")
                print("responseObject = \(responseObject)")
                return
            }
    
            print("responseObject = \(responseObject)")
    }
    

Clearly, in these above, I'm just doing minimal error handling, so you could beef this up, but your question was about issuing POST request, and hopefully the above illustrates how that's done.



回答2:

As @jcaron points out, this post is full of bad edits. There's a lot of variables that have different names later in the function and so on. Not to mention you should NEVER post your api key in a SO question or anywhere on the internet for that matter.

To answer your question on to do a post request in Swift, unless you need incredibly granular control over the process, take a look at Alamofire (same guy that wrote AFNetworking). A POST request is as simple as Alamofire.request(.POST, "http://someapiurl.com") You can also pass in a dictionary of body parameters if you so choose.