URLComponents queryItems теряет процент кодирования при мутации
Когда используешь URLComponents
"s queryItems
Я обнаружил, что если у вас есть элемент запроса, значение которого содержит несколько процентов символов, в моем случае /
кодируется как %2F
тогда, если вы построите URLComponents
объект из String
URL-адрес, содержащий такой элемент запроса, затем измените список элементов запроса для URLComponents
объект, то если вы попытаетесь получить URL
позвонив .url
на URLComponents
объект, то элементы запроса теряют свою процентную кодировку.
Вот код, с которым я тестировал это на детской площадке:
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)
}
Вот изображение, если это облегчает понимание того, что происходит:
2 ответа
Вы должны использовать 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)
}
В RFC 3986 конкретно указано, что URL-запрос может содержать /
персонаж. Это не должно быть закодировано в процентах. URLComponents
просто следуя стандарту и расшифровывая %2F
в /
когда вы специально изменяете любой из параметров запроса.
В первом случае вы ничего не изменяете, поэтому URL остается неизменным. Во втором разделе вы изменяете свойство параметров запроса компонентов. Итак URLComponents
строит новую строку запроса из этого обновленного массива параметров запроса. В процессе, если все они нормализуются и ненужный процент кодировки удаляется.