Объекты URL

Встроенный класс URL предоставляет удобный интерфейс для создания и разбора URL-адресов.

Нет сетевых методов, которые требуют именно объект URL, обычные строки вполне подходят. Так что, технически, мы не обязаны использовать URL. Но иногда он может быть весьма удобным.

Создание URL

Синтаксис создания нового объекта URL:

new URL(url, [base]);
url
– полный URL-адрес или только путь, если указан второй параметр
base
– необязательный «базовый» URL: если указан и аргумент url содержит только путь, то адрес будет создан относительно него.

Например:

let url = new URL('https://javascript.info/profile/admin');

Эти два URL одинаковы:

let url1 = new URL('https://javascript.info/profile/admin');
let url2 = new URL('/profile/admin', 'https://javascript.info');

alert(url1 + '\n' + url2);

Можно легко создать новый URL по пути относительно существующего URL-адреса:

let url = new URL('https://javascript.info/profile/admin');
let newUrl = new URL('tester', url);

alert(newUrl);

Объект URL даёт доступ к компонентам URL, поэтому это отличный способ «разобрать» URL-адрес, например:

let url = new URL('https://javascript.info/url');

alert(url.protocol + '\n'
      url.host + '\n'
      url.pathname);

Вот шпаргалка по компонентам URL:

Мы можем использовать объект URL в методах fetch или XMLHttpRequest и почти во всех других, где ожидается URL-строка.

Вообще, объект URL можно передавать почти куда угодно вместо строки, так как большинство методов сконвертируют объект в строку, при этом он станет строкой с полным URL-адресом.

SearchParams

Допустим, мы хотим создать URL-адрес с заданными параметрами, например, https://google.com/search?query=JavaScript.

Мы можем указать их в строке:

new URL('https://google.com/search?query=JavaScript')

…Но параметры должны быть правильно закодированы, чтобы они могли содержать не-латинские буквы, пробелы и т.п..

Так что для этого есть свойство url.searchParams – объект типа URLSearchParams.

Он предоставляет удобные методы для работы с параметрами:

append(name, value)

добавить параметр по имени,
delete(name)

удалить параметр по имени,
get(name)

получить параметр по имени,
getAll(name)

получить все параметры с одинаковым именем name (такое возможно, например: ?user=John&user=Pete),
has(name)

проверить наличие параметра по имени,
set(name, value)

задать/заменить параметр,
sort()

отсортировать параметры по имени, используется редко,

Пример добавления параметров, содержащих пробелы и знаки препинания:

let url = new URL("https://google.com/search");
var s=url;
url.searchParams.set('q', 'test me!'); // добавим параметр, содержащий пробел и !

s = '\n' + url;            // https://google.com/search?q=test+me%21

url.searchParams.set('tbs', 'qdr:y'); // параметр с двоеточием :

// параметры автоматически кодируются
s += '\n' + url;    // https://google.com/search?query=test+me%21&tbs=qdr%3Ay

// перебрать параметры (в исходном виде)
for(let [name, value] of url.searchParams) {
  s += `\n${name}=${value}`;   // q=test me!, далее tbs=qdr:y
}
alert(s)

Кодирование

Существует стандарт RFC3986, который определяет список разрешённых и запрещённых символов в URL.

Запрещённые символы, например, нелатинские буквы и пробелы, должны быть закодированы – заменены соответствующими кодами UTF-8 с префиксом %, например: %20 (исторически сложилось так, что пробел в URL-адресе можно также кодировать символом +, но это исключение).

К счастью, объекты URL делают всё это автоматически. Мы просто указываем параметры в обычном, незакодированном, виде, а затем конвертируем URL в строку:

let url = new URL("https://ru.wikipedia.org/wiki/Тест");

url.searchParams.set('key', 'ъ');
alert(url); //https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82?key=%D1%8A

Как видно, слово Тест в пути URL-адреса и буква ъ в параметре закодированы.

URL стал длиннее, так как каждая кириллическая буква представляется двумя байтами в кодировке UTF-8.

Кодирование в строках

Раньше, до того как появились объекты URL, люди использовали для URL-адресов обычные строки.

Сейчас URL часто удобнее, но строки всё ещё можно использовать. Во многих случаях код с ними короче.

Однако, если мы используем строку, то надо самим позаботиться о кодировании специальных символов.

Для этого есть встроенные функции:

Возникает естественный вопрос: «Какая разница между encodeURIComponent и encodeURI? Когда использовать одну и другую функцию?»

Это легко понять, если мы посмотрим на URL-адрес, разбитый на компоненты на рисунке выше:

http://site.com:8080/path/page?p1=v1&p2=v2#hash

Как мы видим, в URL-адресе разрешены символы :, ?, =, &, #.

…С другой стороны, если взглянуть на один компонент, например, URL-параметр, то в нём такие символы должны быть закодированы, чтобы не поломать форматирование.

Так что для URL целиком можно использовать encodeURI:

let url = encodeURI("http://site.com/привет");

alert(url); // http://site.com/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82

…А для параметров лучше будет взять encodeURIComponent:

let music = encodeURIComponent("Rock&Roll");

let url = `https://google.com/search?q=${music}`;
alert(url); // https://google.com/search?q=Rock%26Roll

Сравните с encodeURI:

let music = encodeURI('Rock&Roll');

let url = `https://google.com/search?q=${music}`;
alert(url); // https://google.com/search?q=Rock&Roll

Как видим, функция encodeURI не закодировала символ &, который является разрешённым в составе полного URL-адреса.

Но внутри параметра поиска символ & должен быть закодирован, в противном случае мы получим q=Rock&Roll, что значит q=Rock плюс непонятный параметр Roll. Не то, что предполагалось.

Чтобы правильно вставить параметр поиска в строку URL, мы должны использовать для него только encodeURIComponent. Наиболее безопасно кодировать и имя, и значение, за исключением случаев, когда мы абсолютно уверены в том, что они содержат только разрешённые символы.

Классы URL и URLSearchParams базируются на последней спецификации URI, описывающей устройство адресов: RFC3986, в то время как функции encode* – на устаревшей версии стандарта RFC2396.

Различий мало, но они есть, например, по-разному кодируются адреса IPv6:

// допустимый URL-адрес IPv6
let url = 'http://[2607:f8b0:4005:802::1007]/';

alert(encodeURI(url) +      // http://%5B2607:f8b0:4005:802::1007%5D/
      '\n' + new URL(url)); // http://[2607:f8b0:4005:802::1007]/

Как мы видим, функция encodeURI заменила квадратные скобки [...], сделав адрес некорректным. Причина: URL-адреса IPv6 не существовали в момент создания стандарта RFC2396 (август 1998).

Тем не менее, такие случаи редки. По большей части функции encode* работают хорошо.

Источник: learn.javascript.ru/url