这是我将查询参数添加到基本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:)
,我甚至不会遇到这个问题。
答案 0 :(得分:15)
正如其他答案中所指出的,“+”字符在
一个查询字符串,这也在
queryItems
文档。
另一方面, 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)