使用Swift中的URLComponents对“+”进行编码

时间:2017-03-27 17:17:21

标签: ios swift swift3 nsurl

这是我将查询参数添加到基本URL的方式:

let baseURL: URL = ...
let queryParams: [AnyHashable: Any] = ...
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
components?.queryItems = queryParams.map { URLQueryItem(name: $0, value: "\($1)") }
let finalURL = components?.url

当其中一个值包含+符号时,会出现问题。出于某种原因,它在最终网址中未编码为%2B,而是保留+。如果我自己编码并传递%2B,则NSURL会对%进行编码,而'plus'会变为%252B

问题是我如何在%2B

的实例中拥有NSURL

P.S。我知道,如果我自己构造一个查询字符串然后只是将结果传递给NSURL的构造函数init?(string:),我甚至不会遇到这个问题。

4 个答案:

答案 0 :(得分:15)

正如其他答案中所指出的,“+”字符在 一个查询字符串,这也在 query​Items文档。

另一方面, W3C recommendations for URI addressing 说明

  

在查询字符串中,加号被保留为空格的简写表示法。因此,必须编码真正的加号。此方法用于使查询URI更容易在不允许空格的系统中传递。

这可以通过“手动”构建来实现 百分比编码的查询字符串,使用自定义字符集:

let queryParams = ["foo":"a+b", "bar": "a-b", "baz": "a b"]
var components = URLComponents()

var cs = CharacterSet.urlQueryAllowed
cs.remove("+")

components.scheme = "http"
components.host = "www.example.com"
components.path = "/somepath"
components.percentEncodedQuery = queryParams.map {
    $0.addingPercentEncoding(withAllowedCharacters: cs)!
    + "=" + $1.addingPercentEncoding(withAllowedCharacters: cs)!
}.joined(separator: "&")

let finalURL = components.url
// http://www.example.com/somepath?bar=a-b&baz=a%20b&foo=a%2Bb

另一种选择是对生成的加号进行“后编码” 百分比编码的查询字符串:

let queryParams = ["foo":"a+b", "bar": "a-b", "baz": "a b"]
var components = URLComponents()
components.scheme = "http"
components.host = "www.example.com"
components.path = "/somepath"
components.queryItems = queryParams.map { URLQueryItem(name: $0, value: $1) }
components.percentEncodedQuery = components.percentEncodedQuery?
    .replacingOccurrences(of: "+", with: "%2B")

let finalURL = components.url
print(finalURL!)
// http://www.example.com/somepath?bar=a-b&baz=a%20b&foo=a%2Bb

答案 1 :(得分:3)

URLComponents表现正常:+未进行百分比编码,因为它是合法的。您可以使用+ 强制 .alphanumerics进行百分比编码,正如Forest Kunecke已经解释的那样(我独立得到了相同的结果,但他远远超过了我提交他的答案!)。

只是一些改进。如果 是一个字符串,则OP value: "\($1)"是不必要的;你可以说value:$1。并且,最好从其所有组件形成URL。

因此,这与Forest Kunecke基本上是一样的解决方案,但我认为它更规范,最终肯定更紧凑:

let queryParams = ["hey":"ho+ha"]
var components = URLComponents()
components.scheme = "http"
components.host = "www.example.com"
components.path = "/somepath"
components.queryItems = queryParams.map { 
  URLQueryItem(name: $0, 
    value: $1.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!) 
}
let finalURL = components.url

编辑或许更好,也许是在Martin R建议修正后:我们自己形成整个查询并对这些部分进行百分比编码,并告诉URLComponents我们已经这样做了:

let queryParams = ["hey":"ho+ha", "yo":"de,ho"]
var components = URLComponents()
components.scheme = "http"
components.host = "www.example.com"
components.path = "/somepath"
var cs = CharacterSet.urlQueryAllowed
cs.remove("+")
components.percentEncodedQuery = queryParams.map {
    $0.addingPercentEncoding(withAllowedCharacters: cs)! + 
    "=" + 
    $1.addingPercentEncoding(withAllowedCharacters: cs)!
}.joined(separator:"&")

// ---- Okay, let's see what we've got ----
components.queryItems
// [{name "hey", {some "ho+ha"}}, {name "yo", {some "de,ho"}}]
components.url
// http://www.example.com/somepath?hey=ho%2Bha&yo=de,ho

答案 2 :(得分:2)

您可以尝试使用addingPercentEncoding(withAllowedCharacters: .alphanumerics)吗?

我刚刚组建了一个快速游乐场,展示了它的工作原理:

//: Playground - noun: a place where people can play

let baseURL: URL = URL(string: "http://example.com")!
let queryParams: [AnyHashable: Any] = ["test": 20, "test2": "+thirty"]
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)

var escapedComponents = [String: String]()
for item in queryParams {
    let key = item.key as! String
    let paramString = "\(item.value)"

    // percent-encode any non-alphanumeric character.  This is NOT something you typically need to do.  User discretion advised.
    let escaped = paramString.addingPercentEncoding(withAllowedCharacters: .alphanumerics)

    print("escaped: \(escaped)")

    // add the newly escaped components to our dictionary
    escapedComponents[key] = escaped
}


components?.queryItems = escapedComponents.map { URLQueryItem(name: ($0), value: "\($1)") }
let finalURL = components?.url

答案 3 :(得分:0)

插入查询项后,您只需编码components.percentEncodedQuery

let characterSet = CharacterSet(charactersIn: "/+").inverted
components.percentEncodedQuery = components.percentEncodedQuery?.addingPercentEncoding(withAllowedCharacters: characterSet)