如何在 Swift 中转义 HTTP 参数

Posted

技术标签:

【中文标题】如何在 Swift 中转义 HTTP 参数【英文标题】:How to escape the HTTP params in Swift 【发布时间】:2014-11-26 11:33:59 【问题描述】:

我正在尝试向 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 个。

如何转义键和值?

我还能做些什么来改进方法吗?

【问题讨论】:

【参考方案1】:

如果您可以针对 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 的回答,以正确使用过滤器/映射来以一种不错的方式执行编码。

【讨论】:

我同意您的字符集似乎复制了 percentEncodedQuery 所做的事情,但有一个问题:该字符集让 + 未经转义通过! Web 服务器将+ 替换为空格(由x-www-form-urlencoded 规范规定)。 + 应该添加到您的字符中以从安全字符集中删除。我认为 percentEncodedQuery 未能逃脱 + 是值得报告的错误。 好收获。我确实复制了 percentEncodedQuery 所做的事情。 我认为问题在于 NSURLComponents 遵循 RFC3986,它没有说明查询字符串的格式。文档根本没有提到 x-www-form-urlencoded,所以结果可能是预期的......不确定它是否值得出错。如果您确实提交了错误,请在此处粘贴编号! 我非常不同意他们的实现准确地遵循 RFC 3986,其中包括 + 在要进行百分比编码的“保留”字符中,除非“URI 方案明确允许这些字符表示其中的数据组件”(第 2.2 节),至少在查询数据中,+ 绝对不是。该 RFC 将 rintaro 的字符集定义为唯一不应该被百分比转义的字符(第 2.3 节)。这都是学术性的:+ 肯定需要进行百分比转义,否则服务器将替换为空间。我会提交错误报告。 这绝对是迂腐的,但是 RFC 说在查询部分不必编码的字符是 pchar / "/" / "?"其中 pchar 定义为 unreserved / pct-encoded / sub-delims / ":" / "@",而 subdelims 包括 +(和 &,和 =)。显然 &s 和 =s 被编码在键/值中,否则一切都会中断,但我认为你需要一个额外的规范(可能是 x-www-form-urlencoded)来让你编码 +【参考方案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)

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

【讨论】:

您可以这样做:var safeCharacterSet = NSCharacterSet.URLQueryAllowedCharacterSet().mutableCopy(); safeCharacterSet.removeCharactersInString("&=") FWIW,rintaro 的字符集符合 RFC 3986 对“未保留”字符的定义,恕我直言。有关一些参考资料,请参阅 ***.com/a/24888789/1271826。至少,如果您要使用这种removeCharactersInString 方法,请也包含+ 字符。

以上是关于如何在 Swift 中转义 HTTP 参数的主要内容,如果未能解决你的问题,请参考以下文章

Excel:如何在 excel 中转义逗号以在另一个函数(具有多个参数)内传递一个函数(具有多个参数)

如何为大查询制作类似 java 准备语句的查询或如何在大查询中转义参数

在 Visual Studio 调试命令参数中转义

NamedParameterJdbcTemplate Oracle SQL,如何在命名查询中转义单引号?

如何在 CSV 文件中的公式中转义逗号

如何在 sed 中转义斜杠和双引号和单引号?