Az URLPattern: routing reguláris kifejezés-labirintus nélkül

A URLPattern API-val a böngésző natívan tud URL-eket komponensenként illeszteni és elnevezett paramétereket kiolvasni. Megmutatjuk, mikor tisztább a regexnél, és miért nem query parser.

Egy kliensoldali route, egy reverse proxy szabálya és egy webhook-cél URL-jének ellenőrzése gyakran ugyanott végződik: kézzel vágjuk darabokra a pathname-et, vagy írunk egy egyre kevésbé olvasható reguláris kifejezést. A URL Pattern API erre ad böngészőbe épített, URL-tudatos illesztőt. Nem router, hanem egy kis, jól körülhatárolt eszköz arra, hogy eldöntsük: megfelel-e egy URL egy mintának, és ha igen, kiolvassuk belőle a paramétereket.

URL-összetevőket mintákhoz és paraméterekhez kapcsoló sematikus ábra

A név megtévesztően egyszerű: a minta nem csak az útvonalra vonatkozhat. Külön megadható a protokoll, hostnév, port, pathname, query és hash is. Az el nem mondott komponensek wildcardok, ezért például egy csak pathname-re épített minta bármely hoston illeszkedik. Ez kényelmes egy SPA route-nál, de fontos különbség, ha külső URL-t validálunk.

Paraméterek, nem indexelt stringdarabok

A minta nyelvében a :név elnevezett csoportot jelent, a zárójeles rész pedig korlátozhatja azt reguláris kifejezéssel. A konstruktor objektumos alakja akkor különösen olvasható, ha több URL-komponenst vizsgálunk:

const product = new URLPattern({
  pathname: "/termek/:id(\\d+)",
  search: "?ref=:source",
});

const match = product.exec(
  "https://bolt.example/termek/42?ref=hirdetes",
);

if (match) {
  console.log(match.pathname.groups.id);     // "42"
  console.log(match.search.groups.source);  // "hirdetes"
}

A exec() nem egyetlen tömböt ad, mint a RegExp.exec(), hanem minden URL-komponenshez saját találatot. Így a pathname.groups.id és a search.groups.source jelentése a hívás helyén is nyilvánvaló. Ha csak igen/nem kell, a test() felesleges találati objektum nélkül ellenőriz.

A példában a \d+ miatt a /termek/abc nem találat. Ezt korábban sokan a router saját paraméter-parserére bízták, majd egy külön validációban ismételték meg. Itt a route belépési feltétele és a paraméter formája ugyanabban a deklarációban van.

A query nem objektum

A search minta a teljes query komponenst illeszti, nem egy URLSearchParams-szerű kulcs–érték objektumot. Emiatt a fenti ?ref=:source minta a ?ref=hirdetes&kampany=nyar URL-re is talál, de a source értéke hirdetes&kampany=nyar lesz. Ez elsőre meglepő, mégis logikus: a név szerinti csoport a maradék komponenst kapja meg. Ha a paraméterek sorrendje és az opcionális kulcsok nem kötöttek, előbb a URL és a searchParams API-val értelmezzük őket. A URLPattern maradjon a szerkezeti előszűrő, ne próbáljunk vele általános query parser-t írni.

A teljes URL szerkezete számít

A * wildcard, az opcionális és ismétlődő részekhez pedig a csoport utáni ?, * és + használható. Például a /dokumentacio{/*}? illeszti a /dokumentacio és a /dokumentacio/api/v1 útvonalat is. A pontos nyelvtant a WHATWG specifikáció írja le; érdemes abból dolgozni, ha a mintát felhasználói konfigurációból állítjuk elő.

Az API egyik kevésbé látványos, de hasznos tulajdonsága, hogy a legtöbb URL-komponenst alapértelmezésben kis- és nagybetűérzékenyen kezeli. Ez eltérhet egy megszokott framework routertől. Ha valóban ezt akarjuk feloldani, a konstruktor második argumentumában adható { ignoreCase: true }; ne tegyük be reflexből, mert a szerverünk útvonal-szabályai ettől még lehetnek case-sensitive-ek.

Mire jó, és mire nem

Jó helye lehet egy frameworkfüggetlen komponensben, amely linket osztályoz, többféle API URL-t választ szét, vagy egy Service Workerben csak meghatározott erőforrásokra reagál. Web Workerben is elérhető. Ugyanakkor nem kezeli a navigációt, nem renderel oldalt, nem kezeli a böngésző előzményeit, és nem konvertálja a "42" paramétert számmá. Egy teljes router helyett tehát nem érdemes bevezetni, de a routeren kívüli URL-feltételekhez sokkal tisztább, mint a stringműveletek halmaza.

A támogatás ma már praktikus kiindulópont: az MDN Baseline 2025 funkcióként jelöli. Ha régebbi beágyazott böngésző vagy hosszú támogatási időszak van a célplatformon, a kód indulásakor ellenőrizzük a "URLPattern" in globalThis feltételt, és adjunk alternatívát vagy polyfillt. Ne a szerveroldali jogosultság-ellenőrzés egyetlen védelmi vonalaként használjuk: a kliensoldali egyezés csak kliensoldali döntés.

A legjobb mentális modell: a URL az URL-t értelmezi, a URLPattern pedig annak struktúrájára kérdez rá. Ha újra egy slash-ekre darabolt stringet és három egymásba ágyazott regexet írnánk, előbb ezt a két beépített API-t érdemes összerakni.

Források

Leave a Reply

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük