Pattern URL porta il routing alla piattaforma web

Un approccio per standardizzare i casi d'uso comuni di corrispondenza dei pattern.

Sfondo

Il routing è un elemento chiave di ogni applicazione web. In sostanza, il routing consiste nel prendere un URL, applicare una corrispondenza di pattern o un'altra logica specifica per l'app e poi, in genere, visualizzare i contenuti web in base al risultato. Il routing può essere implementato in diversi modi: a volte è codice in esecuzione su un server che mappa un percorso ai file su disco oppure logica in un'app a pagina singola che attende modifiche alla posizione corrente e crea un pezzo corrispondente di DOM da visualizzare.

Sebbene non esista uno standard definitivo, gli sviluppatori web si sono orientati verso una sintassi comune per esprimere pattern di routing degli URL che hanno molto in comune con regular expressions, ma con alcune aggiunte specifiche del dominio come i token per la corrispondenza dei segmenti di percorso. Framework lato server popolari come Express e Ruby on Rails utilizzano questa sintassi (o qualcosa di molto simile) e gli sviluppatori JavaScript possono utilizzare moduli come path-to-regexp o regexpparam per aggiungere questa logica al proprio codice.

URLPattern è un'aggiunta alla piattaforma web che si basa sulle fondamenta create da questi framework. Il suo obiettivo è standardizzare la sintassi dei pattern di routing, incluso il supporto di caratteri jolly, gruppi di token denominati, gruppi di espressioni regolari e modificatori di gruppo. Le istanze URLPattern create con questa sintassi possono eseguire attività di routing comuni, come la corrispondenza con URL completi o un URL pathname, e restituire informazioni sulle corrispondenze di token e gruppi.

Un altro vantaggio della corrispondenza degli URL direttamente nella piattaforma web è che una sintassi comune può essere condivisa con altre API che devono anche corrispondere agli URL.

Supporto dei browser e polyfill

URLPattern è attivato per impostazione predefinita in Chrome e Edge versione 95 e successive.

La libreria urlpattern-polyfill fornisce un modo per utilizzare l'interfaccia URLPattern nei browser o in ambienti come Node che non dispongono del supporto integrato. Se utilizzi il polyfill, assicurati di utilizzare il rilevamento delle funzionalità per caricarlo solo se l'ambiente corrente non lo supporta. In caso contrario, perderai uno dei vantaggi principali di URLPattern: il fatto che gli ambienti di supporto non debbano scaricare e analizzare codice aggiuntivo per utilizzarlo.

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

Compatibilità della sintassi

Una filosofia guida per URLPattern è evitare di reinventare. Se hai già familiarità con la sintassi di routing utilizzata in Express o Ruby on Rails, non dovresti dover imparare nulla di nuovo. Tuttavia, a causa delle lievi divergenze tra le sintassi delle librerie di routing più diffuse, è stato necessario scegliere una sintassi di base e i progettisti di URLPattern hanno deciso di utilizzare la sintassi dei pattern di path-to-regexp (anche se non la sua API) come punto di partenza.

Questa decisione è stata presa dopo un'attenta consultazione con l'attuale maintainer di path-to-regexp.

Il modo migliore per acquisire familiarità con la parte principale della sintassi supportata è consultare la documentazione di path-to-regexp. Puoi leggere la documentazione destinada alla pubblicazione su MDN nella sua casa attuale su GitHub.

Altre funzionalità

La sintassi di URLPattern è un superset di ciò che supporta path-to-regexp, poiché URLPattern supporta una funzionalità insolita tra le librerie di routing: la corrispondenza delle origini, inclusi i caratteri jolly negli hostname. La maggior parte delle altre librerie di routing si occupa solo del pathname e, occasionalmente, della parte di ricerca o hash di un URL. Non devono mai controllare la parte di origine di un URL, in quanto vengono utilizzati solo per il routing con la stessa origine all'interno di un'app web autonoma.

Tenere conto delle origini apre la strada a casi d'uso aggiuntivi, come il routing delle richieste cross-origin all'interno del gestore eventi fetch di un service worker. Se indirizzi solo URL con la stessa origine, puoi ignorarla tranquillamente e utilizzare URLPattern come faresti con altre librerie.

Esempi

Costruzione del pattern

Per creare un URLPattern, passa al suo costruttore stringhe o un oggetto le cui proprietà contengono informazioni sul pattern da confrontare.

Il passaggio di un oggetto offre il controllo più esplicito sul pattern da utilizzare per la corrispondenza di ciascun componente dell'URL. In caso di massima verbosità, può avere il seguente aspetto

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

Se fornisci una stringa vuota per una proprietà, la corrispondenza verrà trovata solo se la parte corrispondente dell'URL non è impostata. Il carattere jolly * corrisponde a qualsiasi valore per una determinata parte dell'URL.

Il costruttore offre diverse scorciatoie per un utilizzo più semplice. Omettere completamente search e hash o qualsiasi altra proprietà equivale a impostarle sul carattere jolly '*'. L'esempio riportato sopra potrebbe essere semplificato in

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

Come scorciatoia aggiuntiva, tutte le informazioni sull'origine possono essere fornite in una singola proprietà, baseURL, che genera

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

Tutti questi esempi presuppongono che il tuo caso d'uso preveda l'associazione di origini. Se ti interessa solo la corrispondenza per le altre parti dell'URL, escludendo l'origine (come accade per molti scenari di routing "tradizionali" con un'unica origine), puoi omettere completamente le informazioni sull'origine e fornire solo qualche combinazione delle proprietà pathname, search e hash. Come in precedenza, le proprietà omesse verranno trattate come se fossero impostate sul pattern * jolly.

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

In alternativa al passaggio di un oggetto al costruttore, puoi fornire una o due stringhe. Se viene fornita una stringa, deve rappresentare un pattern URL completo, incluse le informazioni sul pattern utilizzate per trovare una corrispondenza con l'origine. Se fornisci due stringhe, la seconda viene utilizzata come baseURL e la prima è considerata relativa a questa base.

Indipendentemente dal fatto che vengano fornite una o due stringhe, il costruttore URLPattern analizzerà il pattern URL completo, suddividendolo in componenti URL e mappando ogni parte del pattern più grande al componente corrispondente. Ciò significa che, in un certo senso, ogni URLPattern creato con stringhe viene rappresentato come un URLPattern equivalente creato con un oggetto. Il costruttore di stringhe è solo una scorciatoia per chi preferisce un'interfaccia meno verbosa.

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

Quando utilizzi le stringhe per creare un URLPattern, devi tenere presente alcune avvertenze.

Omettere una proprietà quando si utilizza un oggetto per costruire URLPattern è equivalente a fornire un'espressione generica * per quella proprietà. Quando viene analizzato il pattern di stringa dell'URL completo, se a uno dei componenti dell'URL manca un valore, viene trattato come se la proprietà del componente fosse impostata su '', che corrisponde solo quando il componente è vuoto.

Quando utilizzi le stringhe, devi includere esplicitamente i caratteri jolly se vuoi che vengano utilizzati nel URLPattern creato.

// 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',
});

Tieni inoltre presente che l'analisi di un pattern di stringa nei relativi componenti è potenzialmente ambigua. Esistono caratteri, come :, che si trovano negli URL, ma hanno anche un significato speciale nella sintassi di corrispondenza dei pattern. Per evitare questa ambiguità, il costruttore URLPattern presuppone che uno di questi caratteri speciali faccia parte di un pattern, non dell'URL. Se vuoi che un carattere ambiguo venga interpretato come parte dell'URL, assicurati di eseguirne la fuga con un carattere di escape \` character. For example, the literal URLabout:blankshould be escaped as'about\:blank'` quando viene fornito come stringa.

Utilizzo del pattern

Dopo aver creato un URLPattern, hai due opzioni per utilizzarlo. I metodi test() e exec() accettano entrambi lo stesso input e utilizzano lo stesso algoritmo per verificare la corrispondenza e differiscono solo nel valore restituito. test() restituisce true quando esiste una corrispondenza per l'input specificato e false in caso contrario. exec() restituisce informazioni dettagliate sulla corrispondenza insieme ai gruppi di cattura o null se non esiste alcuna corrispondenza. Gli esempi riportati di seguito mostrano l'utilizzo di exec(), ma puoi sostituire test() con qualsiasi altro se vuoi solo un semplice valore restituito booleano.

Un modo per utilizzare i metodi test() e exec() è passare stringhe. Analogamente a quanto supportato dal costruttore, se viene fornita una singola stringa, deve essere un URL completo, inclusa l'origine. Se vengono fornite due stringhe, la seconda viene trattata come un valore baseURL e la prima viene valutata in base a questa base.

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.

In alternativa, puoi passare lo stesso tipo di oggetto supportato dal costruttore, con proprietà impostate solo sulle parti dell'URL che ti interessano.

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

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

Quando utilizzi exec() in un URLPattern contenente caratteri jolly o token, il valore restituito ti fornirà informazioni sui valori corrispondenti nell'URL di input. In questo modo, non dovrai analizzare autonomamente questi valori.

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'

Gruppi anonimi e con nome

Quando passi una stringa URL a exec(), ricevi un valore che ti indica quali parti corrispondono a tutti i gruppi del pattern.

Il valore restituito ha proprietà che corrispondono ai componenti di URLPattern, come pathname. Pertanto, se un gruppo è stato definito come parte della parte pathname di URLPattern, le corrispondenze possono essere trovate in pathname.groups del valore di ritorno. Le corrispondenze sono rappresentate in modo diverso a seconda che il pattern corrispondente fosse un gruppo anonimo o denominato.

Puoi utilizzare gli indici dell'array per accedere ai valori per una corrispondenza di pattern anonima. Se sono presenti più pattern anonimi, l'indice 0 rappresenterà il valore corrispondente per quello più a sinistra, con 1 e altri indici utilizzati per i pattern successivi.

Quando utilizzi gruppi con nome in un pattern, le corrispondenze verranno esposte come proprietà cuyos nomi corrispondono a ciascun nome di gruppo.

Supporto e normalizzazione di Unicode

URLPattern supporta i caratteri Unicode in diversi modi.

  • I gruppi con nome, come :café, possono contenere caratteri Unicode. Le regole utilizzate per gli identificatori JavaScript validi si applicano ai gruppi denominati.

  • Il testo all'interno di un pattern verrà codificato automaticamente in base alle stesse regole utilizzate per la codifica dell'URL di quel determinato componente. I caratteri Unicode all'interno di pathname verranno codificati in percentuale, quindi un pattern pathname come /café viene normalizzato automaticamente in /caf%C3%A9. I caratteri Unicode in hostname vengono codificati automaticamente utilizzando Punycode anziché la codifica percentuale.

  • I gruppi di espressioni regolari devono contenere solo caratteri ASCII. La sintassi delle espressioni regolari rende difficile e non sicuro codificare automaticamente i caratteri Unicode in questi gruppi. Se vuoi trovare una corrispondenza con un carattere Unicode in un gruppo di espressioni regolari, devi codificarlo in percentuale manualmente, ad esempio (caf%C3%A9) per trovare una corrispondenza con café.

Oltre a codificare i caratteri Unicode, URLPattern esegue anche la normalizzazione dell'URL. Ad esempio, /foo/./bar nel componente pathname viene collapse all'equivalente /foo/bar.

In caso di dubbi su come è stato normalizzato un determinato pattern di input, controlla l'istanza URLPattern costruita utilizzando DevTools del browser.

Riassumendo

La demo di Glitch incorporata di seguito illustra un caso d'uso principale di URLPattern all'interno di fetch event handler di un worker di servizio, mappando pattern specifici a funzioni asincrone che potrebbero generare una risposta alle richieste di rete. I concetti di questo esempio possono essere applicati anche ad altri scenari di routing, sia lato server che lato client.

Feedback e piani futuri

Sebbene la funzionalità di base di URLPattern sia stata implementata in Chrome ed Edge, sono previste altre aggiunte. Alcuni aspetti di URLPattern sono ancora in fase di sviluppo e esistono diverse domande aperte su comportamenti specifici che potrebbero essere ancora perfezionati. Ti invitiamo a provareURLPattern e a inviare eventuali feedback tramite una segnalazione su GitHub.

Supporto per i modelli

La libreria path-to-regexp fornisce un compile() function che inverte efficacemente il comportamento di routing. compile() prende un pattern e i valori per i segnaposto dei token e restituisce una stringa per un percorso URL con i valori sostituiti.

Ci auguriamo di poterlo aggiungere a URLPattern in futuro, ma non rientra nell'ambito della release iniziale.

Attivazione di future funzionalità della piattaforma web

Supponendo che URLPattern diventi una parte consolidata della piattaforma web, altre funzionalità che potrebbero trarre vantaggio dal routing o dalla corrispondenza dei pattern possono essere basate su di essa come elemento primitivo.

Sono in corso discussioni sull'utilizzo di URLPattern per le funzionalità proposte come la corrispondenza di pattern dell'ambito del servizio worker, i PWA come gestori di file e il prefetching speculativo.

Ringraziamenti

Per un elenco completo dei riconoscimenti, consulta il documento esplicativo originale.