簡體   English   中英

在 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: $0, value: "\($1)") }
let finalURL = components?.url

當其中一個值包含+符號時,就會出現問題。 出於某種原因,它沒有在最終 URL 中編碼為%2B ,而是保持為+ 如果我對自己進行編碼並通過%2BNSURL將編碼%並且“加號”變為%252B

問題是如何在NSURL的實例中使用%2B

PS 我知道,如果我自己構造一個查詢字符串,然后簡單地將結果傳遞給NSURL的構造函數init?(string:) ,我什至不會遇到這個問題。

正如其他答案中所指出的,“+”字符在查詢字符串中是有效的,這也在query​Items文檔中說明:

根據 RFC 3986,加號是查詢中的有效字符,不需要進行百分比編碼。 但是,根據W3C 對 URI 尋址的建議,加號被保留為查詢字符串中空格的速記符號(例如, ?greeting=hello+world )。
[...]
根據接收此 URL 的實現,您可能需要預先對加號字符進行百分比編碼。

W3C 對 URI 尋址的建議指出

在查詢字符串中,加號保留為空格的速記符號。 因此,必須對實數加號進行編碼。 此方法用於使查詢 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

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

您可以嘗試使用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

您可以在插入查詢項后簡單地對components.percentEncodedQuery進行編碼。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM