使用 NSURLSession 复制 AFNetworking POST 请求

Posted

技术标签:

【中文标题】使用 NSURLSession 复制 AFNetworking POST 请求【英文标题】:Replicate AFNetworking POST request with NSURLSession 【发布时间】:2015-12-06 21:29:09 【问题描述】:

带有AFNetworking的POST请求:

let urlString = "http://example.com/file.php"
let dictionary = ["key1": [1,2,3], "key2": [2,4,6]]

var error: NSError?
let data = NSJSONSerialization.dataWithJSONObject(dictionary, options: NSJSONWritingOptions.allZeros, error: &error)
let jsonString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let parameters = ["data" :  jsonString!]

let manager = AFHTTPSessionManager()
manager.responseSerializer = AFHTTPResponseSerializer()
manager.POST(urlString, parameters: parameters, success:
    
        requestOperation, response in

        let result = NSString(data: response as! NSData, encoding: NSUTF8StringEncoding)!

        println(result)
    ,
    failure:
    
        requestOperation, error in
)

带有NSURLSession的POST请求:

let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"

let bodyData = NSJSONSerialization.dataWithJSONObject(parameters, options: NSJSONWritingOptions.allZeros, error: &error)!
request.HTTPBody = bodyData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("\(bodyData.length)", forHTTPHeaderField: "Content-Length")

NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler:  (data, response, error) -> Void in
    let result = NSString(data: data, encoding: NSUTF8StringEncoding)!

    println(result)
).resume()

在我拥有的服务器上:

$data = json_decode($_POST["data"], true);
if (!$data) 
    echo "Error: Invalid POST data";
    return;

//do some stuff

echo "success";

在第二种情况下,我得到“错误:无效的 POST 数据”。我做错了什么?

【问题讨论】:

尝试添加这个:request.addValue("application/json", forHTTPHeaderField: "Accept") @DanBeaulieu 如果请求想让服务器知道它会接受 JSON 响应,您可以这样做。但这里的反应不是问题。问题在于请求。 @rob 感谢您的澄清。直到最近我一直在用 Alamofire 作弊,所以我自己是 NSUrl 的新手。 @DanBeaulieu - 使用 Alamofire 不是作弊。这很聪明。查看下面用于百分比转义的笨拙(但必要)代码,您就会开始欣赏这类框架的价值。 @Rob 我同意,但我认为在依靠 3rd 方库之前了解事情的基本原理会更聪明。我会原路返回,回到 Alamofire,我敢肯定。 【参考方案1】:

这是因为 AFNetworking 示例没有创建 JSON 请求,而您的 NSURLSession 示例是。 AFNetworking 示例正在创建一个 application/x-www-form-urlencoded 请求(其中值是您手动创建的 JSON 字符串)。您可以更改服务器代码以接受 JSON 请求或将请求更改为 application/x-www-form-urlencoded 请求。


如果您查看 Charles 之类的 AFNetworking 请求正文,您会看到它生成如下内容:

data=%7B%22key1%22%3A%5B1%2C2%2C3%5D%2C%22key3%22%3A%5B%22Harold%20%26%20Maude%22%5D%2C%22key2%22%3A%5B2%2C4%2C6%5D%7D

如果您对与data 关联的值进行非百分比转义,则有效

data="key1":[1,2,3],"key3":["Harold & Maude"],"key2":[2,4,6]

(注意,我添加了key3 以表明百分比转义是转义标准保留字符,还加上&+。)

如果您想自己使用 NSURLSession 执行此操作,则必须先构建它,然后像这样进行百分比转义:

let allowed = NSCharacterSet.alphanumericCharacterSet().mutableCopy() as! NSMutableCharacterSet
allowed.addCharactersInString("-._~")
let bodyString = "data=" + jsonString.stringByAddingPercentEncodingWithAllowedCharacters(allowed)!

坦率地说,这是一种非常奇怪的方法,将 JSON 嵌入到 application/x-www-form-urlencoded 请求中。我只需更改服务器以接受标准 JSON 请求(完全绕过 $_POST 变量):

$handle = fopen("php://input", "rb");
$raw_post_data = '';
while (!feof($handle)) 
    $raw_post_data .= fread($handle, 8192);

fclose($handle);

$body = json_decode($raw_post_data, true);

顺便说一句,一旦服务器代码接受纯 JSON 请求,Swift 1.x 客户端代码将是:

let request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
request.HTTPMethod = "POST"

request.HTTPBody = NSJSONSerialization.dataWithJSONObject(parameters, options: NSJSONWritingOptions.allZeros, error: &error)!
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler:  (data, response, error) -> Void in
    let result = NSString(data: data, encoding: NSUTF8StringEncoding)!

    println(result)
).resume()

AFNetworking 等效项如下所示:

let urlString = "http://example.com/file.php"
let dictionary = ["key1": [1,2,3], "key2": [2,4,6]]

let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer = AFHTTPResponseSerializer()
manager.POST(urlString, parameters: parameters, success:
    
        requestOperation, response in

        let result = NSString(data: response as! NSData, encoding: NSUTF8StringEncoding)!

        println(result)
    ,
    failure:
    
        requestOperation, error in
)

【讨论】:

以上是关于使用 NSURLSession 复制 AFNetworking POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发之网络编程--1NSURLSession的基本使用

iOS开发之网络编程--1NSURLSession的基本使用

iOS开发系列-NSURLSession

尝试使用 NSURLSession 加载网页

NSURLSession学习

NSURLSession的基本使用