post 请求的 NSURLSession 序列化错误

NSURLSession serialization error with post request

我正在尝试获取 Instagram 的访问令牌,但经常出现错误 You must provide client id我认为这是一个序列化问题,因为我的参数没有正确序列化。 我在 POSTMAN 上试过了,它运行良好,但在 NSURLSession 上它会抛出错误。

错误

{
    code = 400;
    "error_message" = "You must provide a client_id";
    "error_type" = OAuthException;
}

我在序列化时哪里做错了。

let instaInfoDict = NSDictionary(objects: ["client_id","client_secret","grant_type","redirect_uri","code"], forKeys: [kCLientIdInstagram,kCLientSecretIdInstagram,"authorization_code",kRedirectUriInstagram,code])
// Hit post request with params
WebServices().postRequest(kAccessTokenGenerationInstagram, bodyData: instaInfoDict, completionBlock: { (responseData) in
                print(responseData)

})


//WEBSERVICE CLASS:

 func postRequest(url:String, bodyData:NSDictionary, completionBlock:WSCompletionBlock){
        if let url = NSURL(string: url){
            let headers = [
                "Accept": "application/json",
                "content-type": "application/json"]

            let session = NSURLSession.sharedSession()

            let request = NSMutableURLRequest(URL: url, cachePolicy: .UseProtocolCachePolicy, timeoutInterval: 10)
            request.HTTPMethod = "POST"
            request.allHTTPHeaderFields = headers

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

            let task =  session.dataTaskWithRequest(request, completionHandler: { (data, response, error) in
       if error != nil {
            // Handle error


            completionBlock!(responseData:nil)
        }
        else{
//Parsing the data
            let parsedData = parseJson(response as? NSData)

            completionBlock!(responseData: parsedData)

        }

            })

            task.resume()
        }


    }

这个我也试过了

request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(bodyData, options: NSJSONWritingOptions(rawValue: 0))

似乎问题在于 Content-Type 应该是 application/x-www-form-urlencoded 而不是 application/json,而且使用这种 Content-Type 发送参数的方式也有一点变化。

我做了一个小测试,这段代码对我有用:

static func generateAccessToken() {

    let params = ["client_id": "your_id",
                  "client_secret": "your_client_secret",
                  "grant_type": "authorization_code",
                  "redirect_uri": "http://tolkianaa.blogspot.com/",
                  "code": "the_code"]


    guard let url = NSURL(string: "https://api.instagram.com/oauth/access_token") else {
        return
    }
    let request = NSMutableURLRequest(URL: url)
    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    request.HTTPMethod = "POST"

    let stringParams = params.paramsString()
    let dataParams = stringParams.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
    let paramsLength = String(format: "%d", dataParams!.length)
    request.setValue(paramsLength, forHTTPHeaderField: "Content-Length")
    request.HTTPBody = dataParams

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
        var json: AnyObject = [:]

        guard let data = data else {
            return
        }

        do {
            json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
        } catch {
            // Do nothing
        }

        print(json)
    }

    task.resume()
}

为了获取字符串形式的参数,我做了这个扩展:

extension Dictionary {

func paramsString() -> String {
    var paramsString = [String]()
    for (key, value) in self {
        guard let stringValue = value as? String, let stringKey = key as? String else {
            return ""
        }
        paramsString += [stringKey + "=" + "\(stringValue)"]

    }
    return (paramsString.isEmpty ? "" : paramsString.joinWithSeparator("&"))
}

}

希望对您有所帮助!