4 квітня 2022 р.

URL об’єкти

Вбудований клас URL надає зручний інтерфейс для створення та розбирання URL на частини.

Зазвичай, щоб зробити мережевий запит, достатньо передати лише рядок з адресою, передавати саме екземпляр класу URL не має жодної потреби. Тому нас ніхто не зобов’язує використовувати клас URL. Але іноді це може стати в нагоді.

Створення URL

Синтаксис для створення URL об’єктів:

new URL(url, [base])
  • url – повний URL чи, якщо задано другий параметр, тільки шлях (дивись далі),
  • base – необов’язковий параметр з “основою” відносно якої буде побудовано 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); // https://javascript.info/profile/admin
alert(url2); // https://javascript.info/profile/admin

Можна легко створити новий URL із шляху ґрунтуючись на URL, що вже існує:

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

alert(newUrl); // https://javascript.info/profile/tester

Об’єкт URL дозволяє негайно отримати доступ до його складових, тому це зручний спосіб для розбору URL адреси:

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

alert(url.protocol); // https:
alert(url.host);     // javascript.info
alert(url.pathname); // /url

Підказка зі складовими URL об’єкту:

  • href повна URL-адреса, те ж саме, що url.toString()
  • protocol протокол, закінчується символом двокрапки :
  • search – рядок з параметрами, починається символом знаку запитання ?
  • hash починається символом решітки#
  • також можуть бути присутні властивості user та password, якщо використовується формат для HTTP аутентифікації: http://login:password@site.com (не згадано вище, бо рідко використовується).
URL об’єкт можна передати у методи, що використовуються для мережевих запитів замість рядку

fetch чи XMLHttpRequest можуть використовувати URL об’єкти майже всюди, де можна передати рядок з URL.

Зазвичай, URL об’єкт можна передати в будь-який метод замість рядку, оскільки більшість методів перетворять об’єкт в рядок, що містить повну URL-адресу.

Параметри пошуку “?…”

Припустимо, нам потрібно створити URL-адресу з заданими параметрами пошуку, наприклад, https://google.com/search?query=JavaScript.

Ми, звичайно, можемо передати їх в рядку з URL-адресою:

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

…Але параметри повинні бути закодованими, якщо вони містять пробіли, не латинські символи тощо (більше про це нижче).

Отже, для цього URL має властивість: url.searchParams, об’єкт типу URLSearchParams.

Він надає зручні методи для роботи з параметрами пошуку:

  • append(name, value) – додати параметр з іменем name,
  • delete(name) – видалити параметр з іменем name,
  • get(name) – отримати значення параметру з іменем name,
  • getAll(name) – отримати всі параметри, що мають ім’я name (наприклад, ?user=John&user=Pete),
  • has(name) – перевірити чи існує параметр з іменем name,
  • set(name, value) – встановити/замінити параметр з іменем name,
  • sort() – відсортувати параметри за іменем, рідко стає в нагоді,
  • …і це об’єкт також можна перебрати, подібно до Map.

Приклад з параметрами, що містять пробіли та знаки пунктуації:

let url = new URL('https://google.com/search');

url.searchParams.set('q', 'test me!'); // додано параметр з пробілом та !

alert(url); // https://google.com/search?q=test+me%21

url.searchParams.set('tbs', 'qdr:y'); // додано параметр з двокрапкою :

// параметри автоматично закодовано
alert(url); // https://google.com/search?q=test+me%21&tbs=qdr%3Ay

// у циклі перебираємо всі параметри пошуку (кожен параметр автоматично декодується)
for(let [name, value] of url.searchParams) {
  alert(`${name}=${value}`); // q=test me!, then tbs=qdr:y
}

Кодування

Набір символів, що можуть дозволено до використання в URL-адресах, визначено в стандарті RFC3986.

Усі інші символи, що не дозволені стандартом, повинні бути закодовані. Наприклад, не латинські букви та пробіли мають бути замінені на їх UTF-8 коди, що починаються з %. Пробіл буде закодовано у вигляді %20 (з історичних причин пробіл дозволено закодувати як +).

Гарна новина полягає в тому, що URL об’єкт виконає всі перетворення автоматично. Нам потрібно тільки передати всі параметри, а потім перетворити URL в рядок:

// для прикладу використано кириличні символи

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

url.searchParams.set('key', 'ї');
alert(url); // https://uk.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82?key=%D1%97

Як бачите, і Тест у шляху, і параметр ї закодовано.

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

Кодування рядків

До появи URL об’єктів, розробники використовували рядки для URL-адрес.

Наразі, зручніше використовувати URL об’єкти, але рядки все ще можна використовувати. У більшості випадків, використання рядків потребує менше коду.

Слід зауважити, якщо ми використовуємо рядки, то закодувати та декодувати символи нам потрібно вручну.

Для цього є вбудовані функції:

  • encodeURI – закодувати URL-адресу повністю.
  • decodeURI – розкодувати її.
  • encodeURIComponent – закодувати частину URL-адреси, наприклад, параметри пошуку, шлях чи хеш.
  • decodeURIComponent – розкодувати відповідну частину.

Може виникнути природне питання: “Яка різниця між encodeURIComponent та encodeURI? Коли використовувати яку?”

Це легше зрозуміти, якщо подивитися на URL-адресу, що показано розділеною на частини вище.

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

Як бачимо, символи :, ?, =, &, # дозволено безпосередньо використовувати в URL.

…На противагу цьому, якщо ми поглянемо тільки на параметри пошуку URL, то використані там символи повинні бути закодовані, щоб не зламати форматування.

  • encodeURI кодує тільки символи, що заборонені до використання в URL.
  • encodeURIComponent закодує деякі символи та символи: #, $, &, +, ,, /, :, ;, =, ? та @.

Отже, для кодування всього URL можна використати encodeURI:

// використання кириличних символі в шляху url
let url = encodeURI('http://site.com/привіт');

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

…Проте, для URL параметрів нам знадобиться використати 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. Не те, що ми очікували.

Нам слід використовувати тільки encodeURIComponent з параметрами пошуку для правильного вставлення в рядок URL. Для повної безпеки, слід кодувати ім’я та значення параметрів, якщо ми не можемо бути повністю впевненими, що вони містять тільки дозволені символи.

Різниця в кодуванні у порівнянні з URL

Класи URL та URLSearchParams ґрунтуються на останній специфікації URL: RFC3986, але функції encode* використовують застарілу версію RFC2396.

Існують деякі відмінності, як от IPv6 адреси кодуються по-іншому:

// valid url with IPv6 address
let url = 'http://[2607:f8b0:4005:802::1007]/';

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

Як бачимо, encodeURI замінила квадратні дужки [...], що є помилкою, причиною є те, що IPv6 адреси ще не існували в часи створення стандарту RFC2396 (серпень 1998).

Такі випадки рідко трапляються, функції encode* добре справляються в більшості випадків.

Навчальна карта