URLComponents queryItems 在突变时丢失百分比编码
URLComponents queryItems losing percent encoding when mutated
当使用 URLComponents
的 queryItems
时,我发现如果您有一个查询项,其值包含一些百分比编码字符,在我的例子中,/
被编码为%2F
,然后如果您从包含此类查询项的 String
URL 构造一个 URLComponents
对象,则改变 URLComponents
的查询项列表对象,那么如果您尝试通过在 URLComponents
对象上调用 .url
来获取 URL
,则查询项将丢失其百分比编码。
这是我在操场上测试过的代码:
import UIKit
// --- Part 1 ---
print("--- Part 1 ---\n")
let startURL = "https://test.com/test.jpg?X-Test-Token=FQdzEPH%2F%2F%2F"
var components = URLComponents(string: startURL)!
if let compURL = components.url {
print(URL(string: startURL)! == compURL) // True
print(startURL)
print(compURL)
}
// --- Part 2 ---
print("\n--- Part 2 ---\n")
let startURLTwo = "https://test.com/test.jpg?X-Test-Token=FQdzEPH%2F%2F%2F"
let finalURL = "https://test.com/test.jpg?X-Test-Token=FQdzEPH%2F%2F%2F&foo=bar"
var componentsTwo = URLComponents(string: startURLTwo)!
let extraQueryItem = URLQueryItem(name: "foo", value: "bar")
componentsTwo.queryItems!.append(extraQueryItem)
if let compURLTwo = componentsTwo.url {
print(URL(string: finalURL)! == compURLTwo) // False
print(finalURL)
print(compURLTwo)
}
如果可以更容易地理解正在发生的事情,请提供一张图片:
RFC 3986 明确指出 URL 查询可能包含 /
字符。它不需要进行百分比编码。当您专门修改任何查询参数时,URLComponents
只是遵循标准并将 %2F
取消编码为 /
。
在第一种情况下,您根本不修改任何内容,因此 URL 保持不变。第二,修改组件的查询参数属性。因此 URLComponents
从更新的查询参数数组构建一个新的查询字符串。在此过程中,如果将它们全部归一化并删除不必要的百分比编码。
如果您的查询已经进行了百分比编码,您应该使用 percentEncodedQuery
:
let startURL = "https://test.com/test.jpg"
var components = URLComponents(string: startURL)!
components.percentEncodedQuery = "X-Test-Token=FQdzEPH%2F%2F%2F"
if let compURL = components.url {
print(compURL)
}
或者您可以将其指定为未转义(它会保留未转义,因为不必在查询中转义 /
个字符):
let startURL = "https://test.com/test.jpg"
var components = URLComponents(string: startURL)!
components.queryItems = [URLQueryItem(name: "X-Test-Token", value: "FQdzEPH///")]
if let compURL = components.url {
print(compURL)
}
如果您必须更新 queryItems
,请确保在最后设置 percentEncodedQuery
:
let startURL = "https://test.com/test.jpg"
let encodedQuery = "X-Test-Token=FQdzEPH%2F%2F%2F"
var components = URLComponents(string: startURL)!
components.queryItems = [URLQueryItem(name: "foo", value: "bar, baz, & qux")]
if let query = components.percentEncodedQuery {
components.percentEncodedQuery = query + "&" + encodedQuery
} else {
components.percentEncodedQuery = encodedQuery
}
if let compURL = components.url {
print(compURL)
}
当使用 URLComponents
的 queryItems
时,我发现如果您有一个查询项,其值包含一些百分比编码字符,在我的例子中,/
被编码为%2F
,然后如果您从包含此类查询项的 String
URL 构造一个 URLComponents
对象,则改变 URLComponents
的查询项列表对象,那么如果您尝试通过在 URLComponents
对象上调用 .url
来获取 URL
,则查询项将丢失其百分比编码。
这是我在操场上测试过的代码:
import UIKit
// --- Part 1 ---
print("--- Part 1 ---\n")
let startURL = "https://test.com/test.jpg?X-Test-Token=FQdzEPH%2F%2F%2F"
var components = URLComponents(string: startURL)!
if let compURL = components.url {
print(URL(string: startURL)! == compURL) // True
print(startURL)
print(compURL)
}
// --- Part 2 ---
print("\n--- Part 2 ---\n")
let startURLTwo = "https://test.com/test.jpg?X-Test-Token=FQdzEPH%2F%2F%2F"
let finalURL = "https://test.com/test.jpg?X-Test-Token=FQdzEPH%2F%2F%2F&foo=bar"
var componentsTwo = URLComponents(string: startURLTwo)!
let extraQueryItem = URLQueryItem(name: "foo", value: "bar")
componentsTwo.queryItems!.append(extraQueryItem)
if let compURLTwo = componentsTwo.url {
print(URL(string: finalURL)! == compURLTwo) // False
print(finalURL)
print(compURLTwo)
}
如果可以更容易地理解正在发生的事情,请提供一张图片:
RFC 3986 明确指出 URL 查询可能包含 /
字符。它不需要进行百分比编码。当您专门修改任何查询参数时,URLComponents
只是遵循标准并将 %2F
取消编码为 /
。
在第一种情况下,您根本不修改任何内容,因此 URL 保持不变。第二,修改组件的查询参数属性。因此 URLComponents
从更新的查询参数数组构建一个新的查询字符串。在此过程中,如果将它们全部归一化并删除不必要的百分比编码。
如果您的查询已经进行了百分比编码,您应该使用 percentEncodedQuery
:
let startURL = "https://test.com/test.jpg"
var components = URLComponents(string: startURL)!
components.percentEncodedQuery = "X-Test-Token=FQdzEPH%2F%2F%2F"
if let compURL = components.url {
print(compURL)
}
或者您可以将其指定为未转义(它会保留未转义,因为不必在查询中转义 /
个字符):
let startURL = "https://test.com/test.jpg"
var components = URLComponents(string: startURL)!
components.queryItems = [URLQueryItem(name: "X-Test-Token", value: "FQdzEPH///")]
if let compURL = components.url {
print(compURL)
}
如果您必须更新 queryItems
,请确保在最后设置 percentEncodedQuery
:
let startURL = "https://test.com/test.jpg"
let encodedQuery = "X-Test-Token=FQdzEPH%2F%2F%2F"
var components = URLComponents(string: startURL)!
components.queryItems = [URLQueryItem(name: "foo", value: "bar, baz, & qux")]
if let query = components.percentEncodedQuery {
components.percentEncodedQuery = query + "&" + encodedQuery
} else {
components.percentEncodedQuery = encodedQuery
}
if let compURL = components.url {
print(compURL)
}