Pattern URL porta il routing alla piattaforma web

Data di pubblicazione: 22 luglio 2021

Il routing è un elemento chiave di ogni applicazione web. Il routing prende un URL, applica una corrispondenza di pattern o un'altra logica specifica dell'app e poi, di solito, visualizza i contenuti web in base al risultato. Il routing può essere implementato in diversi modi:

  • Codice server che mappa i percorsi ai file su un disco
  • Logica in un'app a una sola pagina che attende le modifiche alla posizione corrente, poi crea e visualizza un elemento DOM corrispondente.

Sebbene non esista uno standard definitivo, gli sviluppatori web hanno adottato una sintassi comune per esprimere i pattern di routing degli URL che condividono molti elementi 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 una sintassi del 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 con un URL pathname, e restituire informazioni sulle corrispondenze di token e gruppi.

Un altro vantaggio di fornire la corrispondenza degli URL direttamente nella piattaforma web è che è possibile condividere una sintassi comune con altre API che devono anche corrispondere agli URL.

Supporto del browser e polyfill

URLPattern è attivato per impostazione predefinita in Chrome ed 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 di supporto integrato. Se utilizzi il polyfill, assicurati di utilizzare il rilevamento delle funzionalità per assicurarti di caricarlo solo se l'ambiente attuale non lo supporta. In caso contrario, perderai uno dei vantaggi principali di URLPattern: il fatto che gli ambienti di assistenza 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 guida per URLPattern è quella di evitare la reinvenzione. Se hai già familiarità con la sintassi di routing utilizzata in Express o Ruby on Rails, non dovresti dover imparare nulla di nuovo. Tuttavia, date le lievi divergenze tra le sintassi nelle 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 relativa superficie API) come punto di partenza.

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

Il modo migliore per acquisire familiarità con la sintassi supportata è consultare la documentazione per path-to-regexp. Puoi leggere la documentazione destinata alla pubblicazione su MDN nella sua attuale posizione su GitHub.

Altre funzionalità

La sintassi di URLPattern è un superset di ciò che supporta path-to-regexp, in quanto URLPattern supporta una funzionalità non comune tra le librerie di routing: la corrispondenza delle origini, inclusi i caratteri jolly nei nomi host. La maggior parte delle altre librerie di routing gestisce solo il pathname e, occasionalmente, la parte search o hash di un URL. Non devono mai controllare la parte di origine di un URL, in quanto vengono utilizzati solo per il routing della 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 multiorigine all'interno di un gestore di eventi fetch di un service worker. Se stai instradando solo URL della stessa origine, puoi ignorare questa funzionalità aggiuntiva e utilizzare URLPattern come 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 ogni componente dell'URL. Nella sua forma più dettagliata, può apparire così:

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 * corrisponderà a qualsiasi valore per una determinata parte dell'URL.

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

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

Come ulteriore scorciatoia, tutte le informazioni sull'origine possono essere fornite in un'unica proprietà, baseURL, il che porta a

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

Tutti questi esempi presuppongono che il tuo caso d'uso preveda origini corrispondenti. Se ti interessa solo la corrispondenza con le altre parti dell'URL, escludendo l'origine (come avviene in molti scenari di routing a singola origine), puoi omettere completamente le informazioni sull'origine e fornire solo una combinazione delle proprietà pathname, search e hash. Come in precedenza, le proprietà omesse verranno trattate come se fossero impostate sul pattern con carattere 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, questa deve 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 venga fornita una o due stringhe, il costruttore URLPattern analizzerà l'intero pattern URL, suddividendolo in componenti URL e mappando ogni parte del pattern più grande al componente corrispondente. Ciò significa che, internamente, ogni URLPattern creato con stringhe viene rappresentato allo stesso modo di 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, ci sono alcune avvertenze da tenere a mente.

Omettere una proprietà quando si utilizza un oggetto per costruire URLPattern equivale a fornire un carattere jolly * per quella proprietà. Quando viene analizzato il pattern della stringa dell'URL completo, se a 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 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',
});

Devi anche essere consapevole che l'analisi di un pattern di stringa nei suoi 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 tutti questi caratteri speciali facciano parte di un pattern, non dell'URL. Se vuoi che un carattere ambiguo venga interpretato come parte dell'URL, assicurati di eseguirne l'escape con un \` character. For example, the literal URLabout:blankshould be escaped as'about\:blank'` quando viene fornito come stringa.

Utilizzare la sequenza

Dopo aver creato un URLPattern, hai due opzioni per utilizzarlo. I metodi test() e exec() prendono lo stesso input e utilizzano lo stesso algoritmo per verificare la corrispondenza e differiscono solo per il valore restituito. test() restituisce true se viene trovata una corrispondenza per l'input specificato, altrimenti restituisce false. exec() restituisce informazioni dettagliate sulla corrispondenza insieme ai gruppi di acquisizione, o null se non è presente alcuna corrispondenza. Gli esempi seguenti mostrano l'utilizzo di exec(), ma puoi sostituirlo con test() in uno qualsiasi di questi esempi se vuoi solo un valore restituito booleano.

Un modo per utilizzare i metodi test() e exec() è passare le 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 stringa viene trattata come valore baseURL e la prima stringa viene valutata in relazione 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 non dovrai analizzare manualmente 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 denominati

Quando passi una stringa URL a exec(), ricevi un valore che 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 porzione pathname di URLPattern, le corrispondenze possono essere trovate in pathname.groups del valore restituito. Le corrispondenze sono rappresentate in modo diverso a seconda che il pattern corrispondente fosse un gruppo anonimo o denominato.

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

Quando utilizzi gruppi denominati in un pattern, le corrispondenze vengono esposte come proprietà i cui nomi corrispondono a ogni nome del gruppo.

Supporto e normalizzazione Unicode

URLPattern supporta i caratteri Unicode in diversi modi.

  • I gruppi denominati, come :café, possono contenere caratteri Unicode. Le regole utilizzate per 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 URL di quel particolare 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 nel 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 manualmente in percentuale, 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 compresso nell'equivalente /foo/bar.

In caso di dubbi sulla normalizzazione di un determinato pattern di input, esamina l'istanza URLPattern creata utilizzando DevTools del browser.

Riassumendo

La demo di Glitch 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 per URLPattern sia stata implementata in Chrome ed Edge, sono previste aggiunte. Alcuni aspetti di URLPattern sono ancora in fase di sviluppo e ci sono diverse questioni aperte su comportamenti specifici che potrebbero essere ancora perfezionati. Ti invitiamo a provare URLPattern e a fornire 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() accetta un pattern e valori per i segnaposto dei token e restituisce una stringa per un percorso URL con i valori sostituiti.

Speriamo di aggiungere questa funzionalità a URLPattern in futuro, ma non rientra nell'ambito della release iniziale.

Attivare le funzionalità future della piattaforma 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 di essa come primitiva.

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

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