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

Art*_*nko 13 nsurl ios swift swift3

这是我如何将查询参数添加到基本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
Run Code Online (Sandbox Code Playgroud)

当其中一个值包含+符号时,就会出现问题.由于某种原因,它没有编码到%2B最终的URL中,而是保留+.如果我自己编码并通过%2B,NSURL编码%并且'加'成为%252B.

问题是我怎么能%2B在实例中NSURL

PS我知道,我甚至不会有这样的问题,如果我构建的查询字符串自己,然后只需一个结果传递给NSURL的构造init?(string:).

Mar*_*n R 21

正如其他答案所指出的,"+"字符在查询字符串中有效,这在query?Items文档中也有说明 .

另一方面,针对URI寻址W3C建议 说明了这一点

在查询字符串中,加号被保留为空格的简写表示法.因此,必须编码真正的加号.此方法用于使查询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
Run Code Online (Sandbox Code Playgroud)

另一种选择是在生成的百分比编码查询字符串中"加编码"加号字符:

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
Run Code Online (Sandbox Code Playgroud)


mat*_*att 9

URLComponents 行为正确:+未进行百分比编码,因为它目前是合法的。您可以强制+是百分比编码通过.alphanumerics,通过森林Kunecke已经解释(我独立得到了相同的结果,但他远远超过了我在提交他的回答!)。

只是一些改进。value: "\($1)"如果这一个字符串,则不需要OP ;你可以说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
Run Code Online (Sandbox Code Playgroud)

编辑也许更好,在 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
Run Code Online (Sandbox Code Playgroud)


小智 6

您可以components.percentEncodedQuery在插入查询项后简单地进行编码。

let characterSet = CharacterSet(charactersIn: "/+").inverted
components.percentEncodedQuery = components.percentEncodedQuery?.addingPercentEncoding(withAllowedCharacters: characterSet)
Run Code Online (Sandbox Code Playgroud)