如何在Swift中转义HTTP参数

时间:2014-11-26 11:34:00

标签: ios swift code-injection

我正在尝试向HTTP服务器发帖

这是我的代码:

  func sendPostToUrl(url:String, withParams params: [String: String?] ) {
    var request = NSMutableURLRequest(URL: NSURL(string: url)!)
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    var err: NSError?
    var bodyData = ""
    for (key,value) in params{
        if (value==nil){ continue }
        let scapedKey = key.stringByAddingPercentEncodingWithAllowedCharacters(
                 .URLHostAllowedCharacterSet())!
        let scapedValue = value!.stringByAddingPercentEncodingWithAllowedCharacters(
                .URLHostAllowedCharacterSet())!
        bodyData += "\(scapedKey)=\(scapedValue)&"
    }
    request.HTTPBody = bodyData.dataUsingEncoding
               (NSUTF8StringEncoding, allowLossyConversion: true)

    var task = session.dataTaskWithRequest(request, 
    completionHandler: {data, response, error -> Void in
        println("Response: \(response)")
        let dataString = NSString(data: data, encoding: NSUTF8StringEncoding)
        println("Data: \(dataString)")

    })
    task.resume()
}

它有效,但并不完美。如果我以这种方式调用函数:

    client.sendPostToUrl("http://novagecko.com/tests/test.php", 
        withParams: ["hello":"world","inject":"param1=value1&param2=value2"]);

服务器检测到3个帖子字段(包含密钥helloinjectparam2)而不是2。

如何逃避键和值?

我还有什么可以做的改进方法吗?

2 个答案:

答案 0 :(得分:10)

如果您可以定位iOS 8(感谢@Rob),请使用NSURLComponents来转义您的参数:

import Foundation

func encodeParameters(#params: [String: String]) -> String {
    var queryItems = map(params) { NSURLQueryItem(name:$0, value:$1)}
    var components = NSURLComponents()
    components.queryItems = queryItems
    return components.percentEncodedQuery ?? ""
}

现在encodeParameters(params:["hello":"world","inject":"param1=value1&param2=value2"])会像您期望的那样返回hello=world&inject=param1%3Dvalue1%26param2%3Dvalue2

否则,创建可让您正确转义值的字符集的最佳方法是:

var safeCharacterSet = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy()
safeCharacterSet.removeCharactersInString("&=")

并查看@ rintaro的答案,正确使用filter / map以良好的方式执行编码。

答案 1 :(得分:2)

似乎NSCharacterSet没有相关的设置。

所以,添加这个

extension NSCharacterSet {
    class func URLUnreservedCharacterSet() -> NSCharacterSet {
        return self(charactersInString: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_.~")
    }
}

然后

var bodyData = ""
var safeCharacterSet = NSCharacterSet.URLUnreservedCharacterSet()
for (key,value) in params{
    if (value==nil){ continue }
    let scapedKey = key.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
    let scapedValue = value!.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
    bodyData += "\(scapedKey)=\(scapedValue)&"
}

按照@ Rob的评论建议,这里有mapjoin示例:

let params:[String:String?] = ["fubar":nil, "hello":"world", "inject":"param1=value1&param2=value2"]

let safeCharacterSet = NSCharacterSet.URLUnreservedCharacterSet()
let pairs = filter(params, {$1 != nil}).map { (key, value) -> String in
    let _key = key.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
    let _val = value!.stringByAddingPercentEncodingWithAllowedCharacters(safeCharacterSet)!
    return _key + "=" + _val
}
let bodyData = "&".join(pairs)

这样更好,因为结果中没有尾随&