URLPattern ajoute le routage à la plate-forme Web

Approche permettant de standardiser les cas d'utilisation courants de la correspondance de modèles.

Contexte

Le routage est un élément clé de chaque application Web. Le routage consiste principalement à prendre une URL, à lui appliquer une correspondance de structure ou à lui appliquer une autre logique spécifique à l'application, puis, généralement, à afficher du contenu Web en fonction du résultat. Le routage peut être mis en œuvre de plusieurs manières: il s'agit parfois de code exécuté sur un serveur qui mappe un chemin d'accès aux fichiers sur disque ou de la logique d'une application monopage qui attend les modifications de l'emplacement actuel et crée un élément DOM correspondant à afficher.

Bien qu'il n'existe pas de norme définitive, les développeurs Web se sont tournés vers une syntaxe commune pour exprimer des modèles de routage d'URL qui partagent beaucoup de points communs avec regular expressions, mais avec des ajouts spécifiques à un domaine tels que des jetons pour la mise en correspondance des segments de chemin d'accès. Les frameworks côté serveur populaires tels que Express et Ruby on Rails utilisent cette syntaxe (ou une méthode très proche). Les développeurs JavaScript peuvent utiliser des modules tels que path-to-regexp ou regexpparam pour ajouter cette logique à leur propre code.

URLPattern est un complément à la plate-forme Web qui s'appuie sur la base créée par ces frameworks. Son objectif est de standardiser la syntaxe d'un modèle de routage, y compris la prise en charge des caractères génériques, des groupes de jetons nommés, des groupes d'expressions régulières et des modificateurs de groupe. Les instances URLPattern créées avec cette syntaxe peuvent effectuer des tâches de routage courantes, telles que la mise en correspondance avec des URL complètes ou une URL pathname, et renvoyer des informations sur les correspondances du jeton et du groupe.

Fournir une correspondance d'URL directement sur la plate-forme Web présente un autre avantage : une syntaxe commune peut ensuite être partagée avec d'autres API qui doivent également correspondre aux URL.

Prise en charge des navigateurs et polyfills

URLPattern est activé par défaut dans Chrome et Edge 95 ou version ultérieure.

La bibliothèque urlpattern-polyfill permet d'utiliser l'interface URLPattern dans des navigateurs ou des environnements tels que Node qui ne sont pas compatibles. Si vous utilisez le polyfill, veillez à utiliser la détection de caractéristiques pour vous assurer de ne le charger que si l'environnement actuel n'est pas pris en charge. Sinon, vous perdrez l'un des principaux avantages de URLPattern: le fait que les environnements d'assistance n'aient pas besoin de télécharger ni d'analyser de code supplémentaire pour pouvoir l'utiliser.

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

Compatibilité de la syntaxe

La philosophie directrice de URLPattern consiste à éviter les réinventions. Si vous connaissez déjà la syntaxe de routage utilisée dans Express ou Ruby on Rails, vous ne devriez pas avoir à apprendre quelque chose de nouveau. Toutefois, étant donné les légères différences entre les syntaxes dans les bibliothèques de routage populaires, une syntaxe a dû être choisie. Les concepteurs de URLPattern ont donc décidé d'utiliser la syntaxe de modèle de path-to-regexp (mais pas de sa surface d'API) comme point de départ.

Cette décision a été prise après une étroite consultation avec le responsable actuel du maintien de path-to-regexp.

Le meilleur moyen de vous familiariser avec le cœur de la syntaxe acceptée est de consulter la documentation sur path-to-regexp. Vous pouvez consulter la documentation destinée à être publiée sur MDN dans sa page d'accueil actuelle sur GitHub.

Autres fonctionnalités

La syntaxe de URLPattern est un sur-ensemble de ce que path-to-regexp prend en charge, car URLPattern accepte une fonctionnalité peu commune parmi les bibliothèques de routage: faire correspondre les origines, y compris les caractères génériques dans les noms d'hôte. La plupart des autres bibliothèques de routage ne traitent que du chemin d'accès, et parfois de la partie recherche ou hachage d'une URL. Ils n'ont jamais besoin de vérifier la partie origine d'une URL, car ils ne sont utilisés que pour le routage de la même origine dans une application Web autonome.

La prise en compte des origines ouvre la voie à d'autres cas d'utilisation, tels que le routage de requêtes multi-origines dans le gestionnaire d'événements fetch d'un service worker. Si vous n'achetez que des URL de même origine, vous pouvez ignorer cette fonctionnalité supplémentaire et utiliser URLPattern comme les autres bibliothèques.

Exemples

Construire le modèle

Pour créer un URLPattern, transmettez à son constructeur des chaînes ou un objet dont les propriétés contiennent des informations sur le modèle à mettre en correspondance.

La transmission d'un objet offre le contrôle le plus explicite sur le format à utiliser pour faire correspondre chaque composant d'URL. À son niveau le plus détaillé, cela peut ressembler à

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

Lorsque vous fournissez une chaîne vide pour une propriété, une correspondance n'est établie que si la partie correspondante de l'URL n'est pas définie. Le caractère générique * correspond à n'importe quelle valeur pour une partie donnée de l'URL.

Le constructeur propose plusieurs raccourcis pour simplifier l'utilisation. Le fait d'omettre complètement search et hash (ou toute autre propriété) équivaut à les définir sur le caractère générique '*'. L'exemple ci-dessus pourrait être simplifié pour

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

En outre, toutes les informations sur l'origine peuvent être fournies dans une seule propriété, baseURL, menant à

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

Tous ces exemples supposent que votre cas d'utilisation implique des origines correspondantes. Si vous souhaitez mettre en correspondance uniquement les autres parties de l'URL, en excluant l'origine (comme c'est le cas pour de nombreux scénarios "traditionnels" de routage à origine unique), vous pouvez omettre complètement les informations sur l'origine et fournir simplement une combinaison des propriétés pathname, search et hash. Comme précédemment, les propriétés omises seront traitées comme si elles avaient été définies sur le format générique *.

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

Au lieu de transmettre un objet au constructeur, vous pouvez fournir une ou deux chaînes. Si une chaîne est fournie, elle doit représenter un format d'URL complet, y compris les informations de format utilisées pour établir une correspondance avec l'origine. Si vous fournissez deux chaînes, la seconde est utilisée en tant que baseURL, et la première est considérée comme relative à cette base.

Qu'une ou deux chaînes soient fournies, le constructeur URLPattern analyse le format d'URL complet, le décompose en composants d'URL, puis mappe chaque partie du plus grand format au composant correspondant. Cela signifie que, en arrière-plan, chaque URLPattern créé avec des chaînes finit par être représenté de la même manière qu'un URLPattern équivalent créé avec un objet. Le constructeur de chaînes n'est qu'un raccourci, pour ceux qui préfèrent une interface moins détaillée.

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

Lorsque vous utilisez des chaînes pour créer un URLPattern, gardez à l'esprit certaines mises en garde.

Si vous omettez une propriété lors de l'utilisation d'un objet pour construire URLPattern, cela équivaut à fournir un caractère générique * pour cette propriété. Lors de l'analyse du format de chaîne d'URL complète, s'il manque une valeur à l'un des composants de l'URL, la propriété du composant est traitée comme si la propriété du composant était définie sur ''. La correspondance n'est établie que lorsque ce composant est vide.

Lorsque vous utilisez des chaînes, vous devez inclure explicitement les caractères génériques si vous souhaitez qu'ils soient utilisés dans le URLPattern construit.

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

Sachez également que l'analyse d'un modèle de chaîne dans ses composants est potentiellement ambiguë. Certains caractères, comme :, se trouvent dans les URL, mais ont également une signification particulière dans la syntaxe de correspondance de modèles. Pour éviter cette ambiguïté, le constructeur URLPattern suppose que l'un de ces caractères spéciaux fait partie d'un format et non de l'URL. Si vous souhaitez qu'un caractère ambigu soit interprété dans l'URL, assurez-vous de l'échapper avec \` character. For example, the literal URLabout:blankshould be escaped as'about\:blank'` lorsqu'il est fourni sous forme de chaîne.

Utiliser le schéma

Après avoir construit une URLPattern, vous avez deux options pour l'utiliser. Les méthodes test() et exec() prennent la même entrée et utilisent le même algorithme pour rechercher une correspondance, et ne diffèrent que par leur valeur renvoyée. test() renvoie true en cas de correspondance pour l'entrée donnée, et false dans le cas contraire. exec() renvoie des informations détaillées sur la correspondance ainsi que des groupes de capture, ou null en l'absence de correspondance. Les exemples suivants illustrent l'utilisation de exec(), mais vous pouvez remplacer test() par n'importe lequel si vous ne souhaitez qu'une valeur renvoyée booléenne simple.

Pour utiliser les méthodes test() et exec(), vous pouvez transmettre des chaînes. De la même manière que le constructeur est compatible, si une seule chaîne est fournie, il doit s'agir d'une URL complète, incluant l'origine. Si deux chaînes sont fournies, la seconde est traitée comme une valeur baseURL et la première est évaluée comme relative à cette 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.

Vous pouvez également transmettre le même type d'objet que celui accepté par le constructeur, avec des propriétés définies uniquement sur les parties de l'URL dont vous souhaitez mettre en correspondance.

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

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

Lorsque vous utilisez exec() sur un URLPattern contenant des caractères génériques ou des jetons, la valeur renvoyée vous fournit des informations sur les valeurs correspondantes dans l'URL d'entrée. Cela peut vous éviter d'avoir à analyser ces valeurs vous-même.

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'

Groupes anonymes et nommés

Lorsque vous transmettez une chaîne d'URL à exec(), vous obtenez une valeur indiquant les parties qui correspondent à tous les groupes du modèle.

La valeur renvoyée possède des propriétés correspondant aux composants de URLPattern, comme pathname. Ainsi, si un groupe a été défini dans la partie pathname de URLPattern, les correspondances sont disponibles dans l'élément pathname.groups de la valeur renvoyée. Les correspondances sont représentées différemment selon que le modèle correspondant était un groupe anonyme ou nommé.

Vous pouvez utiliser des index de tableau pour accéder aux valeurs d'une correspondance de structure anonyme. S'il existe plusieurs modèles anonymes, l'index 0 représente la valeur correspondante pour le modèle le plus à gauche, avec 1 et d'autres index utilisés pour les modèles suivants.

Lorsque vous utilisez des groupes nommés dans un modèle, les correspondances sont exposées en tant que propriétés dont les noms correspondent à chaque nom de groupe.

Compatibilité et normalisation avec Unicode

URLPattern accepte les caractères Unicode de différentes manières.

  • Les groupes nommés, comme :café, peuvent contenir des caractères Unicode. Les règles utilisées pour les identifiants JavaScript valides s'appliquent aux groupes nommés.

  • Le texte d'un format sera automatiquement encodé selon les mêmes règles que celles utilisées pour l'encodage URL d'un composant particulier. Les caractères Unicode dans pathname sont encodés en pourcentage. Par conséquent, un modèle pathname tel que /café est automatiquement normalisé en /caf%C3%A9. Les caractères Unicode dans la section hostname sont automatiquement encodés à l'aide du code Punycode, plutôt qu'au moyen d'un encodage de pourcentage.

  • Les groupes d'expressions régulières ne doivent contenir que des caractères ASCII. La syntaxe d'expression régulière rend difficile et non fiable l'encodage automatique des caractères Unicode dans ces groupes. Si vous souhaitez mettre en correspondance un caractère Unicode dans un groupe d'expressions régulières, vous devez l'encoder manuellement en pourcentage, par exemple (caf%C3%A9) pour correspondre à café.

En plus d'encoder les caractères Unicode, URLPattern effectue également la normalisation des URL. Par exemple, /foo/./bar dans le composant pathname est réduit au format /foo/bar équivalent.

En cas de doute sur la façon dont un modèle d'entrée donné a été normalisé, inspectez l'instance URLPattern construite à l'aide des DevTools de votre navigateur.

Synthèse

La démonstration de Glitch intégrée ci-dessous illustre un cas d'utilisation principal de URLPattern dans le fetch event handler d'un service worker, en mappant des modèles spécifiques à des fonctions asynchrones susceptibles de générer une réponse aux requêtes réseau. Les concepts de cet exemple peuvent également être appliqués à d'autres scénarios de routage, côté serveur ou côté client.

Commentaires et projets futurs

Bien que les fonctionnalités de base de URLPattern aient été intégrées à Chrome et Edge, des ajouts sont prévus. Certains aspects de URLPattern sont en cours de développement, et un certain nombre de questions ouvertes sur certains comportements spécifiques peuvent encore être affinées. Nous vous encourageons à essayer URLPattern et à nous faire part de vos commentaires via un problème GitHub.

Prise en charge de la création de modèles

La bibliothèque path-to-regexp fournit un compile() function qui inverse efficacement le comportement de routage. compile() prend un format et des valeurs pour les espaces réservés de jeton, et renvoie une chaîne pour un chemin d'URL dans laquelle ces valeurs sont remplacées.

Nous espérons l'ajouter à URLPattern à l'avenir, mais cela n'entre pas dans le champ d'application de la version initiale.

Activation des futures fonctionnalités de la plate-forme Web

En supposant que URLPattern devienne une partie établie de la plate-forme Web, d'autres fonctionnalités qui pourraient bénéficier du routage ou de la correspondance de modèles peuvent s'appuyer dessus en tant que primitives.

Des discussions sont en cours sur l'utilisation de URLPattern pour les fonctionnalités proposées, telles que la correspondance de modèles de champ d'application de service worker, les PWA en tant que gestionnaires de fichiers et le préchargement spéculatif.

Remerciements

Consultez le document d'explication d'origine pour obtenir la liste complète des accusés de réception.