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 строит новую строку запроса из этого обновленного массива параметров запроса. В процессе, если все они нормализуются и ненужный процент кодировки удаляется.

Другие вопросы по тегам