URLPattern ajoute le routage à la plate-forme Web

Une approche visant à normaliser les cas d'utilisation courants de la mise en correspondance de modèles.

Le routage est un élément clé de toute application Web. À la base, le routage consiste à prendre une URL, à lui appliquer une correspondance de format ou 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 façons: il peut s'agir d'un code exécuté sur un serveur qui mappe un chemin d'accès à des fichiers sur disque, ou d'une logique dans 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 sont attirés par une syntaxe commune pour exprimer des formats de routage d'URL en commun avec regular expressions, mais avec des ajouts spécifiques à un domaine, comme 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 quelque chose de 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 ajout à la plate-forme Web qui s'appuie sur la base créée par ces frameworks. Son objectif est de normaliser une syntaxe de 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, comme la mise en correspondance avec des URL complètes ou une URL pathname, et renvoyer des informations sur les correspondances de jeton et de groupe.

Un autre avantage de la mise en correspondance des URL directement sur la plate-forme Web est qu'une syntaxe commune peut ensuite être partagée avec d'autres API qui doivent également faire correspondre des URL.

Navigateurs compatibles et polyfills

URLPattern est activé par défaut dans Chrome et Edge versions 95 et ultérieures.

La bibliothèque urlpattern-polyfill permet d'utiliser l'interface URLPattern dans les navigateurs ou les environnements tels que Node, qui ne sont pas compatibles avec cette interface. Si vous utilisez le polyfill, assurez-vous d'utiliser la détection de fonctionnalités pour vous assurer de ne le charger que si l'environnement actuel n'est pas compatible. 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 et d'analyser du code supplémentaire pour l'utiliser.

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

Compatibilité de la syntaxe

La philosophie sous-jacente de URLPattern consiste à éviter de se réinventer. Si vous connaissez déjà la syntaxe de routage utilisée dans Express ou Ruby on Rails, vous ne devriez pas avoir à apprendre de nouvelles choses. Toutefois, étant donné les légères divergences entre les syntaxes des bibliothèques de routage populaires, il a fallu choisir une syntaxe de base. Les concepteurs de URLPattern ont décidé d'utiliser la syntaxe de modèle de path-to-regexp (mais pas sa surface d'API) comme point de départ.

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

Le meilleur moyen de vous familiariser avec le cœur de la syntaxe compatible est de consulter la documentation sur path-to-regexp. Vous pouvez lire la documentation destinée à être publiée sur MDN dans son emplacement actuel sur GitHub.

Autres fonctionnalités

La syntaxe de URLPattern est un sur-ensemble de ce que path-to-regexp accepte, car URLPattern prend en charge une fonctionnalité inhabituelle parmi les bibliothèques de routage: la mise en correspondance des origines, y compris des caractères génériques dans les noms d'hôte. La plupart des autres bibliothèques de routage ne traitent que le pathname, et parfois 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.

Prendre en compte les origines ouvre la voie à d'autres cas d'utilisation, comme l'acheminement des requêtes inter-origines dans le gestionnaire d'événements fetch d'un service worker. Si vous ne routagez que des URL de même origine, vous pouvez ignorer cette fonctionnalité supplémentaire et utiliser URLPattern comme les autres bibliothèques.

Exemples

Créer 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 format à faire correspondre.

Transmettre un objet offre le contrôle le plus explicite sur le format à utiliser pour faire correspondre chaque composant d'URL. Dans sa forme la plus détaillée, cela peut ressembler à

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

Si vous fournissez une chaîne vide pour une propriété, elle ne correspondra 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 une utilisation plus simple. Omettre complètement search et hash, ou toute autre propriété, revient à les définir sur l'espace réservé '*'. L'exemple ci-dessus peut être simplifié comme suit :

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

En guise de raccourci supplémentaire, toutes les informations sur l'origine peuvent être fournies dans une seule propriété, baseURL, ce qui donne

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

Tous ces exemples partent du principe que votre cas d'utilisation implique des origines correspondantes. Si vous souhaitez uniquement mettre en correspondance 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 simplement fournir 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 étaient définies sur le modèle de caractère 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 seule chaîne est fournie, elle doit représenter un format d'URL complet, y compris les informations de format utilisées pour faire correspondre l'origine. Si vous fournissez deux chaînes, la deuxième chaîne est utilisée comme baseURL, et la première chaîne est considérée comme relative à cette base.

Que vous fournissiez une ou deux chaînes, le constructeur URLPattern analysera le format d'URL complet, le divisant en composants d'URL et mappant chaque partie du format plus grand sur le composant correspondant. Cela signifie que sous le capot, 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, destiné aux utilisateurs 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 quelques points.

Omettre une propriété lorsque vous utilisez un objet pour créer URLPattern équivaut à fournir un caractère générique * pour cette propriété. Lorsque le modèle de chaîne d'URL complète est analysé, si une valeur est manquante dans l'un des composants de l'URL, il est traité comme si la propriété du composant était définie sur '', ce qui ne correspondra qu'à ce composant lorsqu'il sera 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 créé.

// 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 mise en correspondance de format. 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é comme faisant partie de l'URL, veillez à l'échapper avec un \` character. For example, the literal URLabout:blankshould be escaped as'about\:blank'` lorsqu'il est fourni en tant que chaîne.

Utiliser le modèle

Après avoir créé un URLPattern, vous avez deux options pour l'utiliser. Les méthodes test() et exec() utilisent toutes les deux la même entrée et le même algorithme pour vérifier une correspondance. Elles ne diffèrent que par leur valeur de retour. test() renvoie true en cas de correspondance avec 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 cas de non-correspondance. Les exemples suivants montrent comment utiliser exec(), mais vous pouvez remplacer test() par l'un d'eux si vous ne souhaitez qu'une valeur de retour booléenne simple.

Pour utiliser les méthodes test() et exec(), vous pouvez transmettre des chaînes. Comme pour le constructeur, 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 deuxième chaîne est traitée comme une valeur baseURL, et la première chaîne est évaluée par rapport à 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 le constructeur, avec des propriétés définies uniquement sur les parties de l'URL qui vous intéressent.

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 indique quelles étaient 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.

Les propriétés de la valeur renvoyée correspondent aux composants de URLPattern, comme pathname. Par conséquent, si un groupe a été défini dans la partie pathname de URLPattern, les correspondances peuvent être trouvées dans la 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 celui le plus à gauche, tandis que 1 et d'autres indices sont 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é avec Unicode et normalisation

URLPattern est compatible avec 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 modèle est automatiquement encodé selon les mêmes règles que celles utilisées pour l'encodage URL de ce composant particulier. Les caractères Unicode dans pathname seront 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 de hostname sont automatiquement encodés à l'aide de Punycode, plutôt qu'encodés en pourcentage.

  • Les groupes d'expressions régulières ne doivent contenir que des caractères ASCII. La syntaxe des expressions régulières rend l'encodage automatique des caractères Unicode dans ces groupes difficile et dangereux. Si vous souhaitez faire correspondre un caractère Unicode dans un groupe d'expression régulière, vous devez l'encoder en pourcentage manuellement, 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 à l'/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 créée à l'aide des DevTools de votre navigateur.

Synthèse

La démonstration 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 pouvant générer une réponse aux requêtes réseau. Les concepts de cet exemple peuvent également s'appliquer à d'autres scénarios d'acheminement, côté serveur ou côté client.

Commentaires et projets futurs

Bien que les fonctionnalités de base de URLPattern soient disponibles dans Chrome et Edge, des ajouts sont prévus. Certains aspects de URLPattern sont en cours de développement, et il existe un certain nombre de questions ouvertes sur des comportements spécifiques qui peuvent encore être affinés. Nous vous encourageons à essayer URLPattern et à nous faire part de vos commentaires en répondant à un problème GitHub.

Prise en charge des modèles

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

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

Activer les 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 pouvant bénéficier du routage ou de la mise en correspondance de modèles peuvent s'appuyer dessus en tant que primitive.

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

Remerciements

Pour obtenir la liste complète des remerciements, consultez le document explicatif d'origine.