URLPattern brengt routing naar het webplatform

Een aanpak voor het standaardiseren van veelvoorkomende gebruiksscenario's voor patroonmatching.

Achtergrond

Routing is een belangrijk onderdeel van elke webapplicatie. In de kern houdt routering in dat je een URL neemt, er patroonmatching of andere app-specifieke logica op toepast en vervolgens, meestal, webinhoud weergeeft op basis van het resultaat. Routering kan op een aantal manieren worden geïmplementeerd: het is soms code die op een server draait en die een pad naar bestanden op schijf toewijst, of logica in een app van één pagina die wacht op wijzigingen in de huidige locatie en een corresponderend stukje DOM creëert om weergave.

Hoewel er niet één definitieve standaard bestaat, zijn webontwikkelaars aangetrokken tot een gemeenschappelijke syntaxis voor het uitdrukken van URL-routeringspatronen die veel gemeen hebben met regular expressions , maar met enkele domeinspecifieke toevoegingen zoals tokens voor het matchen van padsegmenten. Populaire server-side frameworks zoals Express en Ruby on Rails gebruiken deze syntaxis (of iets dat daar heel dicht bij in de buurt komt), en JavaScript-ontwikkelaars kunnen modules zoals path-to-regexp of regexpparam gebruiken om die logica aan hun eigen code toe te voegen.

URLPattern is een toevoeging aan het webplatform dat voortbouwt op de basis die door deze frameworks is gelegd. Het doel is om de syntaxis van een routeringspatroon te standaardiseren, inclusief ondersteuning voor jokertekens, benoemde tokengroepen, reguliere expressiegroepen en groepsmodificatoren. URLPattern instanties die met deze syntaxis zijn gemaakt, kunnen algemene routeringstaken uitvoeren, zoals matchen met volledige URL's of een URL- pathname , en informatie retourneren over het token en groepsovereenkomsten.

Een ander voordeel van het rechtstreeks aanbieden van URL-matching op het webplatform is dat een gemeenschappelijke syntaxis vervolgens kan worden gedeeld met andere API's die ook moeten matchen met URL's.

Browserondersteuning en polyfills

URLPattern is standaard ingeschakeld in Chrome en Edge versie 95 en hoger.

De urlpattern-polyfill bibliotheek biedt een manier om de URLPattern interface te gebruiken in browsers of omgevingen zoals Node die geen ingebouwde ondersteuning hebben. Als u de polyfill gebruikt, zorg er dan voor dat u functiedetectie gebruikt om ervoor te zorgen dat u deze alleen laadt als de huidige omgeving geen ondersteuning biedt. Anders verliest u een van de belangrijkste voordelen van URLPattern : het feit dat ondersteuningsomgevingen geen aanvullende code hoeven te downloaden en te parseren om deze te kunnen gebruiken.

if (!(globalThis && 'URLPattern' in globalThis)) {
  // URLPattern is not available, so the polyfill is needed.
}

Syntaxiscompatibiliteit

Een leidende filosofie voor URLPattern is om heruitvinding te voorkomen. Als u al bekend bent met de routeringssyntaxis die wordt gebruikt in Express of Ruby on Rails, hoeft u niets nieuws te leren. Maar gezien de kleine verschillen tussen syntaxis in populaire routeringsbibliotheken, moest er iets worden gekozen als basissyntaxis, en de ontwerpers van URLPattern besloten om de patroonsyntaxis van path-to-regexp (maar niet het API-oppervlak) als uitgangspunt te gebruiken. .

Deze beslissing is genomen na nauw overleg met de huidige beheerder van path-to-regexp .

De beste manier om vertrouwd te raken met de kern van de ondersteunde syntaxis is door de documentatie voor path-to-regexp te raadplegen. U kunt de documentatie die bedoeld is voor publicatie op MDN lezen in de huidige thuisbasis op GitHub.

Extra functies

De syntaxis van URLPattern is een superset van wat path-to-regexp ondersteunt, aangezien URLPattern een ongebruikelijke functie onder routeringsbibliotheken ondersteunt: matching origins , inclusief jokertekens in hostnamen. De meeste andere routeringsbibliotheken behandelen alleen de padnaam en af ​​en toe het zoek- of hashgedeelte van een URL. Ze hoeven nooit het oorsprongsgedeelte van een URL te controleren, omdat ze alleen worden gebruikt voor routering van dezelfde oorsprong binnen een op zichzelf staande web-app.

Door rekening te houden met de oorsprong wordt de deur geopend voor aanvullende gebruiksscenario's, zoals het routeren van cross-origin-verzoeken binnen de fetch van een servicemedewerker . Als u alleen URL's van dezelfde oorsprong doorstuurt, kunt u deze extra functie effectief negeren en URLPattern gebruiken zoals andere bibliotheken.

Voorbeelden

Het patroon construeren

Om een URLPattern te maken, geeft u de constructor ervan strings of een object door waarvan de eigenschappen informatie bevatten over het patroon waarmee moet worden vergeleken.

Het doorgeven van een object biedt de meest expliciete controle over welk patroon moet worden gebruikt voor het matchen van elke URL-component. In zijn meest uitgebreide vorm kan dit er zo uitzien

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
  search: '*',
  hash: '*',
});

Het opgeven van een lege tekenreeks voor een eigenschap komt alleen overeen als het overeenkomstige deel van de URL niet is ingesteld. Het jokerteken * komt overeen met elke waarde voor een bepaald deel van de URL.

De constructor biedt verschillende snelkoppelingen voor eenvoudiger gebruik. Het volledig weglaten van search en hash , of andere eigenschappen, staat gelijk aan het instellen ervan op het jokerteken '*' . Het bovenstaande voorbeeld kan worden vereenvoudigd tot

const p = new URLPattern({
  protocol: 'https',
  username: '',
  password: '',
  hostname: 'example.com',
  port: '',
  pathname: '/foo/:image.jpg',
});

Als extra snelkoppeling kan alle informatie over de oorsprong worden verstrekt in één enkele eigenschap, baseURL , die leidt naar

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

Bij al deze voorbeelden wordt ervan uitgegaan dat uw gebruiksscenario het matchen van de oorsprong omvat. Als u alleen geïnteresseerd bent in matching op de andere delen van de URL, met uitzondering van de oorsprong (zoals het geval is voor veel "traditionele" routeringsscenario's met één oorsprong), dan kunt u de oorsprongsinformatie volledig weglaten en slechts een combinatie opgeven van de eigenschappen pathname , search en hash . Net als voorheen worden weggelaten eigenschappen behandeld alsof ze zijn ingesteld op het jokertekenpatroon * .

const p = new URLPattern({pathname: '/foo/:image.jpg'});

Als alternatief voor het doorgeven van een object aan de constructor kunt u één of twee strings opgeven. Als er één tekenreeks wordt opgegeven, moet deze een volledig URL-patroon vertegenwoordigen, inclusief patrooninformatie die wordt gebruikt om overeen te komen met de oorsprong. Als u twee tekenreeksen opgeeft, wordt de tweede tekenreeks gebruikt als baseURL en wordt de eerste tekenreeks beschouwd als relatief ten opzichte van die basis.

Of er nu één of twee tekenreeksen worden opgegeven, de URLPattern constructor parseert het volledige URL-patroon, verdeelt het in URL-componenten en wijst elk deel van het grotere patroon toe aan de overeenkomstige component. Dit betekent dat onder de motorkap elk URLPattern dat met tekenreeksen is gemaakt, uiteindelijk op dezelfde manier wordt weergegeven als een equivalent URLPattern dat met een object is gemaakt. De strings-constructor is slechts een snelkoppeling, voor degenen die de voorkeur geven aan een minder uitgebreide interface.

const p = new URLPattern('https://example.com/foo/:image.jpg?*#*');

Wanneer u strings gebruikt om een URLPattern te maken, zijn er enkele kanttekeningen waarmee u rekening moet houden.

Het weglaten van een eigenschap bij het gebruik van een object om URLPattern te construeren, komt overeen met het opgeven van een jokerteken * voor die eigenschap. Wanneer het volledige URL-tekenreekspatroon wordt geparseerd en een van de URL-componenten een waarde mist, wordt deze behandeld alsof de eigenschap van de component is ingesteld op '' , wat alleen overeenkomt als die component leeg is.

Wanneer u tekenreeksen gebruikt, moet u de jokertekens expliciet opnemen als u wilt dat ze worden gebruikt in het geconstrueerde URLPattern .

// p1 and p2 are equivalent.
const p1 = new URLPattern('/foo', location.origin);
const p2 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
  search: '',
  hash: '',
});

// p3 and p4 are equivalent.
const p3 = new URLPattern('/foo?*#*', location.origin);
const p4 = new URLPattern({
  protocol: location.protocol,
  hostname: location.hostname,
  pathname: '/foo',
});

Houd er ook rekening mee dat het ontleden van een tekenreekspatroon in zijn componenten mogelijk dubbelzinnig is. Er zijn tekens, zoals : , die voorkomen in URL's, maar die ook een speciale betekenis hebben in de syntaxis van patroonvergelijking. Om deze dubbelzinnigheid te voorkomen, gaat de URLPattern constructor ervan uit dat al deze speciale tekens deel uitmaken van een patroon en niet van de URL. Als u wilt dat een dubbelzinnig teken wordt geïnterpreteerd als onderdeel van de URL, zorg er dan voor dat u er een \` character. For example, the literal URL should be escaped as 'about\:blank'` wanneer deze als tekenreeks wordt opgegeven.

Het patroon gebruiken

Nadat u een URLPattern heeft gemaakt, heeft u twee opties om deze te gebruiken. De methoden test() en exec() gebruiken beide dezelfde invoer en gebruiken hetzelfde algoritme om te controleren op een overeenkomst, en verschillen alleen in hun geretourneerde waarde. test() retourneert true als er een overeenkomst is voor de gegeven invoer, en false anders. exec() retourneert gedetailleerde informatie over de match samen met capture-groepen, of null als er geen match is. De volgende voorbeelden demonstreren het gebruik van exec() , maar u kunt test() voor elk ervan inruilen als u alleen een eenvoudige Booleaanse retourwaarde wilt.

Eén manier om de methoden test() en exec() te gebruiken is door tekenreeksen door te geven. Vergelijkbaar met wat de constructor ondersteunt: als er een enkele tekenreeks wordt opgegeven, moet deze een volledige URL zijn, inclusief de oorsprong. Als er twee tekenreeksen zijn opgegeven, wordt de tweede tekenreeks behandeld als een baseURL waarde en wordt de eerste tekenreeks geëvalueerd als relatief ten opzichte van die basis.

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

const result = p.exec('https://example.com/foo/cat.jpg');
// result will contain info about the successful match.
// const result = p.exec('/foo/cat.jpg', 'https://example.com')
// is equivalent, using the baseURL syntax.

const noMatchResult = p.exec('https://example.com/bar');
// noMatchResult will be null.

Als alternatief kunt u hetzelfde soort object doorgeven dat de constructor ondersteunt, met eigenschappen die zijn ingesteld op alleen de delen van de URL die u belangrijk vindt om te matchen.

const p = new URLPattern({pathname: '/foo/:image.jpg'});

const result = p.exec({pathname: '/foo/:image.jpg'});
// result will contain info about the successful match.

Wanneer u exec() gebruikt op een URLPattern die jokertekens of tokens bevat, geeft de geretourneerde waarde u informatie over wat de overeenkomstige waarden waren in de invoer-URL. Dit kan u de moeite besparen om die waarden zelf te moeten ontleden.

const p = new URLPattern({
  hostname: ':subdomain.example.com',
  pathname: '/*/:image.jpg'
});

const result = p.exec('https://imagecdn1.example.com/foo/cat.jpg');
// result.hostname.groups.subdomain will be 'imagecdn1'
// result.pathname.groups[0] will be 'foo', corresponding to *
// result.pathname.groups.image will be 'cat'

Anonieme en benoemde groepen

Wanneer u een URL-tekenreeks doorgeeft aan exec() , krijgt u een waarde terug die aangeeft welke delen overeenkomen met alle groepen van het patroon.

De geretourneerde waarde heeft eigenschappen die overeenkomen met de componenten van URLPattern , zoals pathname . Dus als een groep is gedefinieerd als onderdeel van het pathname van URLPattern , kunnen de overeenkomsten worden gevonden in de retourwaarde pathname.groups . De overeenkomsten worden verschillend weergegeven, afhankelijk van of het overeenkomstige patroon een anonieme of benoemde groep was.

U kunt array-indexen gebruiken om toegang te krijgen tot de waarden voor een anonieme patroonovereenkomst. Als er meerdere anonieme patronen zijn, vertegenwoordigt index 0 de overeenkomende waarde voor het meest linkse patroon, waarbij 1 en verdere indices worden gebruikt voor volgende patronen.

Wanneer benoemde groepen in een patroon worden gebruikt, worden de overeenkomsten weergegeven als eigenschappen waarvan de namen overeenkomen met elke groepsnaam.

Unicode-ondersteuning en normalisatie

URLPattern ondersteunt Unicode-tekens op een aantal verschillende manieren.

  • Benoemde groepen, zoals :café , kunnen Unicode-tekens bevatten. De regels die worden gebruikt voor geldige JavaScript-ID's zijn van toepassing op benoemde groepen.

  • Tekst binnen een patroon wordt automatisch gecodeerd volgens dezelfde regels die worden gebruikt voor URL-codering van dat specifieke onderdeel. Unicode-tekens binnen pathname zijn procentgecodeerd , dus een pathname zoals /café wordt automatisch genormaliseerd naar /caf%C3%A9 . Unicode-tekens in de hostname worden automatisch gecodeerd met Punycode in plaats van met procentcodering.

  • Reguliere expressiegroepen mogen alleen ASCII-tekens bevatten. De syntaxis van reguliere expressies maakt het moeilijk en onveilig om Unicode-tekens in deze groepen automatisch te coderen. Als u een Unicode-teken in een reguliere expressiegroep wilt matchen, moet u het handmatig procentueel coderen, bijvoorbeeld (caf%C3%A9) om overeen te komen met café .

Naast het coderen van Unicode-tekens, voert URLPattern ook URL-normalisatie uit. /foo/./bar in de pathname wordt bijvoorbeeld samengevouwen tot het equivalent /foo/bar .

Als u twijfelt over hoe een bepaald invoerpatroon is genormaliseerd, inspecteer dan de geconstrueerde URLPattern instantie met behulp van de DevTools van uw browser.

Alles op een rij zetten

De onderstaande Glitch-demo illustreert een kerngebruiksscenario van URLPattern in de fetch event handler van een servicemedewerker, waarbij specifieke patronen worden toegewezen aan asynchrone functies die een reactie op netwerkverzoeken kunnen genereren. De concepten in dit voorbeeld kunnen ook worden toegepast op andere routeringsscenario's, zowel aan de serverzijde als aan de clientzijde.

Feedback en toekomstplannen

Hoewel de basisfunctionaliteit voor URLPattern in Chrome en Edge is opgenomen, zijn er toevoegingen gepland. Sommige aspecten van URLPattern zijn nog in ontwikkeling en er zijn een aantal open vragen over specifiek gedrag die mogelijk nog verder worden verfijnd. We raden u aan URLPattern uit te proberen en feedback te geven via een GitHub-probleem .

Ondersteuning voor sjablonen

De path-to-regexp -bibliotheek biedt een compile() function die het routeringsgedrag effectief omkeert. compile() neemt een patroon en waarden voor de tijdelijke aanduidingen voor het token, en retourneert een tekenreeks voor een URL-pad waarin deze waarden zijn vervangen.

We hopen dit in de toekomst aan URLPattern toe te voegen , maar dit valt niet binnen de reikwijdte van de eerste release.

Toekomstige webplatformfuncties mogelijk maken

Ervan uitgaande dat URLPattern een gevestigd onderdeel van het webplatform wordt, kunnen andere functies die baat zouden kunnen hebben bij routering of patroonmatching er als primitief bovenop bouwen.

Er zijn voortdurende discussies over het gebruik van URLPattern voor voorgestelde functies, zoals het matchen van scopepatronen van servicemedewerkers , PWA's als bestandshandlers en speculatief prefetchen .

Dankbetuigingen

Zie het originele uitlegdocument voor een volledige lijst met dankbetuigingen.