Pattern URL porta il routing alla piattaforma web

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

Contesto

Il routing è un elemento chiave di ogni applicazione web. Essenzialmente, il routing comporta l'acquisizione di un URL, l'applicazione di una corrispondenza di pattern o di un'altra logica specifica dell'app e, di solito, la visualizzazione di contenuti web in base al risultato. Il routing può essere implementato in diversi modi: a volte a volte il codice è in esecuzione su un server che mappa un percorso ai file su disco oppure la logica in un'app a pagina singola che attende le modifiche alla posizione attuale e crea una parte corrispondente del DOM da visualizzare.

Sebbene non esista uno standard definitivo, gli sviluppatori web si sono concentrati su una sintassi comune per esprimere pattern di routing degli URL che condividono molto in comune con regular expressions, ma con alcune aggiunte specifiche del dominio, come i token per i segmenti di percorso corrispondenti. I framework lato server più diffusi come Express e Ruby on Rails utilizzano questa sintassi (o qualcosa di molto simile) e gli sviluppatori JavaScript possono usare moduli come path-to-regexp o regexpparam per aggiungere questa logica al proprio codice.

URLPattern è un'aggiunta alla piattaforma web che si basa sulle basi create da questi framework. Il suo obiettivo è standardizzare la sintassi di un pattern di routing, compreso 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 ricerca di corrispondenze con URL completi o un URL pathname, e la restituzione di informazioni sulle corrispondenze di token e gruppi.

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

Supporto del browser e polyfill

L'app URLPattern è attiva per impostazione predefinita in Chrome ed Edge 95 e versioni successive.

La libreria urlpattern-polyfill offre un modo per utilizzare l'interfaccia URLPattern in browser o ambienti come Node, che non supportano integrato. Se utilizzi il polyfill, assicurati di utilizzare il rilevamento delle funzionalità per assicurarti di caricarlo solo se l'ambiente attuale non supporta il supporto. In caso contrario, perderai uno dei principali vantaggi di URLPattern: il fatto che gli ambienti di supporto non devono 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 alla base di URLPattern è evitare di reinventarsi. Se già conosci la sintassi di routing utilizzata in Express o Ruby su Rails, non dovresti imparare niente di nuovo. Tuttavia, date le lievi differenze tra le sintassi nelle librerie di routing più diffuse, è stato necessario scegliere una sintassi di base, pertanto i designer di URLPattern hanno deciso di utilizzare come punto di partenza la sintassi del pattern di path-to-regexp (anche se non la sua superficie API).

Questa decisione è stata presa dopo una stretta consultazione con l'attuale gestore della rete di path-to-regexp.

Il modo migliore per acquisire familiarità con il nucleo della sintassi supportata è consulta la documentazione per path-to-regexp. Puoi leggere la documentazione prevista per la pubblicazione su MDN nella sua attuale home page su GitHub.

Altre funzionalità

La sintassi di URLPattern è un soprainsieme di ciò che path-to-regexp supporta, poiché URLPattern supporta una funzionalità non comune tra le librerie di routing: le origini corrispondenti, inclusi i caratteri jolly nei nomi host. La maggior parte delle altre librerie di routing si occupa solo del pathname e occasionalmente della parte search o hash di un URL. Non devono mai controllare la parte di origine di un URL, poiché vengono utilizzati solo per il routing dalla stessa origine all'interno di un'app web autonoma.

Prendere in considerazione le origini apre le porte ad altri casi d'uso, come il routing delle richieste multiorigine all'interno del gestore di eventi fetch di un service worker. Se esegui il routing solo di URL della stessa origine, puoi ignorare questa funzionalità aggiuntiva e utilizzare URLPattern come altre librerie.

Esempi

Costruire il pattern

Per creare un oggetto URLPattern, trasmettine il costruttore o le stringhe oppure un oggetto le cui proprietà contengono informazioni sul pattern con cui eseguire un confronto.

La trasmissione di un oggetto offre il controllo più esplicito sul pattern da utilizzare per la corrispondenza con ogni componente URL. Nella forma più dettagliata, può sembrare

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

La specifica di una stringa vuota per una proprietà corrisponderà solo se la parte corrispondente dell'URL non è impostata. Il carattere jolly * corrisponderà a qualsiasi valore per una determinata porzione dell'URL.

Il costruttore offre diverse scorciatoie per un utilizzo più semplice. Omettere completamente search e hash, o qualsiasi altra proprietà, equivale a impostarli sul carattere jolly '*'. L'esempio precedente può essere semplificato per

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 un'unica proprietà, baseURL, che rimanda a

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

Tutti questi esempi presuppongono che il tuo caso d'uso implichi le corrispondenze delle origini. Se ti interessa la corrispondenza solo nelle altre parti dell'URL, escludendo l'origine (come nel caso di molti scenari di routing "tradizionali" a origine singola), puoi omettere completamente le informazioni sull'origine e fornire solo una combinazione delle proprietà pathname, search e hash. Come prima, le proprietà omesse verranno trattate come se fossero impostate sul pattern di caratteri 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, dovrebbe rappresentare un pattern URL completo, incluse le informazioni sul pattern utilizzate per la corrispondenza con l'origine. Se fornisci due stringhe, la seconda viene utilizzata come baseURL e la prima viene considerata relativa a questa base.

Indipendentemente dal fatto che sia stata fornita una o due stringhe, il costruttore URLPattern analizzerà l'intero pattern URL, suddividendolo in componenti URL e mappa ogni parte del pattern più grande al componente corrispondente. Ciò significa che in background, 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 dettagliata.

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

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

Eliminare una proprietà quando si utilizza un oggetto per creare URLPattern equivale a fornire un carattere jolly * per quella proprietà. Quando viene analizzato il pattern stringa dell'URL completo, se in uno dei componenti dell'URL manca un valore, viene considerato come se la proprietà del componente fosse impostata su '', che corrisponderà solo quando il componente è vuoto.

Quando utilizzi le stringhe, devi includere esplicitamente i caratteri jolly se vuoi che vengano utilizzati nella build 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',
});

Tieni presente, inoltre, che l'analisi di un pattern stringa nei suoi componenti è potenzialmente ambigua. Negli URL sono presenti caratteri, come :, che hanno un significato speciale nella sintassi di corrispondenza del pattern. Per evitare questa ambiguità, il costruttore URLPattern presuppone che uno qualsiasi 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 specificare un carattere di escape \` character. For example, the literal URLabout:blankshould be escaped as"about\:blank"" quando viene fornito come stringa.

Utilizzo del pattern

Una volta creato URLPattern, hai due opzioni per utilizzarlo. I metodi test() e exec() accettano entrambi lo stesso input e utilizzano lo stesso algoritmo per verificare una corrispondenza, 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 acquisizione oppure null in assenza di corrispondenze. I seguenti esempi mostrano l'uso di exec(), ma puoi sostituire test() con uno qualsiasi se vuoi solo un semplice valore restituito booleano.

Un modo per utilizzare i metodi test() e exec() è trasmettere le stringhe. Analogamente a quanto supportato dal costruttore, se viene fornita una singola stringa, questa deve essere un URL completo, inclusa l'origine. Se vengono fornite due stringhe, la seconda stringa 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 per la corrispondenza.

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() su un URLPattern che contiene caratteri jolly o token, il valore restituito ti fornirà informazioni sui valori corrispondenti nell'URL di input. In questo modo, puoi evitare di dover analizzare questi valori autonomamente.

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 denominati

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

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

Puoi utilizzare gli indici di 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 denominati in un pattern, le corrispondenze verranno mostrate come proprietà i cui nomi corrispondono al nome di ciascun gruppo.

Supporto e normalizzazione 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 vengono applicate ai gruppi denominati.

  • Il testo all'interno di un pattern verrà codificato automaticamente in base alle stesse regole utilizzate per la codifica URL di quel particolare componente. I caratteri Unicode all'interno di pathname saranno codificati a 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 dell'espressione regolare rende difficile e non sicura la codifica automatica dei caratteri Unicode in questi gruppi. Se vuoi trovare la corrispondenza di un carattere Unicode in un gruppo di espressioni regolari, devi codificarlo manualmente tramite percentuale, ad esempio (caf%C3%A9) per trovare la corrispondenza con café.

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

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

Riassumendo

La demo di Glitch incorporata di seguito illustra un caso d'uso principale di URLPattern all'interno di un service worker fetch event handler, 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, lato server o lato client.

Feedback e piani futuri

Sebbene la funzionalità di base di URLPattern sia arrivata su Chrome ed Edge, sono previste aggiunte. Alcuni aspetti di URLPattern sono ancora in fase di sviluppo e ci sono diverse domande aperte su comportamenti specifici che potrebbero essere ancora perfezionati. Ti invitiamo a provare URLPattern e a fornire feedback tramite un problema di GitHub.

Supporto per i modelli

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

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

Attivazione delle funzionalità delle future piattaforme web

Supponendo che URLPattern diventi una parte consolidata della piattaforma web, altre funzionalità che potrebbero trarre vantaggio dal routing o dalla corrispondenza di pattern possono basarsi su questo elemento come primitiva.

Sono in corso discussioni sull'utilizzo di URLPattern per le funzionalità proposte, come la corrispondenza dei pattern degli ambiti dei service worker, le PWA come gestori di file e il precaricamento speculativo.

Ringraziamenti

Consulta il documento esplicativo originale per un elenco completo dei riconoscimenti.