在 Swift 中使用 URLComponents 对“+”进行编码
Encode '+' using URLComponents in Swift
这就是我向基础添加查询参数的方式 URL:
let baseURL: URL = ...
let queryParams: [AnyHashable: Any] = ...
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
components?.queryItems = queryParams.map { URLQueryItem(name: [=11=], value: "\()") }
let finalURL = components?.url
当其中一个值包含 +
符号时,就会出现问题。由于某种原因,它没有在最终 URL 中编码为 %2B
,而是保持 +
。如果我自己编码并传递 %2B
,NSURL
编码 %
并且 'plus' 变为 %252B
.
问题是如何在 NSURL
的实例中得到 %2B
?
P.S。我知道,如果我自己构造一个查询字符串然后简单地将结果传递给 NSURL
的构造函数 init?(string:)
.
,我什至不会遇到这个问题
你可以尝试使用 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: ([=10=]), value: "\()") }
let finalURL = components?.url
URLComponents 行为正确:+
未进行百分比编码,因为它目前是合法的。您可以 强制 +
使用 .alphanumerics
进行百分比编码,正如 Forest Kunecke 已经解释的那样(我独立得到了相同的结果,但他遥遥领先我提交他的答案!)。
只是一些改进。如果 是 一个字符串,OP 的 value: "\()"
是不必要的;你可以直接说 value:
。而且,最好由所有组件组成 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: [=10=],
value: .addingPercentEncoding(withAllowedCharacters: .alphanumerics)!)
}
let finalURL = components.url
EDIT 更好,也许,在 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 {
[=11=].addingPercentEncoding(withAllowedCharacters: cs)! +
"=" +
.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
正如其他答案中所指出的,“+”字符在
一个查询字符串,这也在
queryItems
文档:
According to RFC 3986, the plus sign is a valid character within a query, and doesn't need to be percent-encoded. However, according to the W3C recommendations for URI addressing, the plus sign is reserved as shorthand notation for a space within a query string (for example, ?greeting=hello+world
).
[...]
Depending on the implementation receiving this URL, you may need to preemptively percent-encode the plus sign character.
还有
W3C recommendations for URI addressing
说明
Within the query string, the plus sign is reserved as shorthand notation for a space. Therefore, real plus signs must be encoded. This method was used to make query URIs easier to pass in systems which did not allow spaces.
这可以通过“手动”构建来实现
百分比编码查询字符串,使用自定义字符集:
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 {
[=10=].addingPercentEncoding(withAllowedCharacters: cs)!
+ "=" + .addingPercentEncoding(withAllowedCharacters: cs)!
}.joined(separator: "&")
let finalURL = components.url
// http://www.example.com/somepath?bar=a-b&baz=a%20b&foo=a%2Bb
另一种选择是“post-encode”生成的加号
百分比编码的查询字符串:
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: [=11=], value: ) }
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
您可以在插入查询项后简单地编码 components.percentEncodedQuery
。
let characterSet = CharacterSet(charactersIn: "/+").inverted
components.percentEncodedQuery = components.percentEncodedQuery?.addingPercentEncoding(withAllowedCharacters: characterSet)
这就是我向基础添加查询参数的方式 URL:
let baseURL: URL = ...
let queryParams: [AnyHashable: Any] = ...
var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: false)
components?.queryItems = queryParams.map { URLQueryItem(name: [=11=], value: "\()") }
let finalURL = components?.url
当其中一个值包含 +
符号时,就会出现问题。由于某种原因,它没有在最终 URL 中编码为 %2B
,而是保持 +
。如果我自己编码并传递 %2B
,NSURL
编码 %
并且 'plus' 变为 %252B
.
问题是如何在 NSURL
的实例中得到 %2B
?
P.S。我知道,如果我自己构造一个查询字符串然后简单地将结果传递给 NSURL
的构造函数 init?(string:)
.
你可以尝试使用 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: ([=10=]), value: "\()") }
let finalURL = components?.url
URLComponents 行为正确:+
未进行百分比编码,因为它目前是合法的。您可以 强制 +
使用 .alphanumerics
进行百分比编码,正如 Forest Kunecke 已经解释的那样(我独立得到了相同的结果,但他遥遥领先我提交他的答案!)。
只是一些改进。如果 是 一个字符串,OP 的 value: "\()"
是不必要的;你可以直接说 value:
。而且,最好由所有组件组成 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: [=10=],
value: .addingPercentEncoding(withAllowedCharacters: .alphanumerics)!)
}
let finalURL = components.url
EDIT 更好,也许,在 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 {
[=11=].addingPercentEncoding(withAllowedCharacters: cs)! +
"=" +
.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
正如其他答案中所指出的,“+”字符在
一个查询字符串,这也在
queryItems
文档:
According to RFC 3986, the plus sign is a valid character within a query, and doesn't need to be percent-encoded. However, according to the W3C recommendations for URI addressing, the plus sign is reserved as shorthand notation for a space within a query string (for example,
?greeting=hello+world
).
[...]
Depending on the implementation receiving this URL, you may need to preemptively percent-encode the plus sign character.
还有 W3C recommendations for URI addressing 说明
Within the query string, the plus sign is reserved as shorthand notation for a space. Therefore, real plus signs must be encoded. This method was used to make query URIs easier to pass in systems which did not allow spaces.
这可以通过“手动”构建来实现 百分比编码查询字符串,使用自定义字符集:
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 {
[=10=].addingPercentEncoding(withAllowedCharacters: cs)!
+ "=" + .addingPercentEncoding(withAllowedCharacters: cs)!
}.joined(separator: "&")
let finalURL = components.url
// http://www.example.com/somepath?bar=a-b&baz=a%20b&foo=a%2Bb
另一种选择是“post-encode”生成的加号 百分比编码的查询字符串:
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: [=11=], value: ) }
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
您可以在插入查询项后简单地编码 components.percentEncodedQuery
。
let characterSet = CharacterSet(charactersIn: "/+").inverted
components.percentEncodedQuery = components.percentEncodedQuery?.addingPercentEncoding(withAllowedCharacters: characterSet)