URLPattern ajoute le routage à la plate-forme Web

Publié le 22 juillet 2021

Le routage est un élément clé de toute application Web. Au fond, le routage prend une URL, lui applique une logique de correspondance de modèle ou une autre logique spécifique à l'application, puis affiche généralement du contenu Web en fonction du résultat. Le routage peut être implémenté de différentes manières :

  • Code serveur qui mappe les chemins d'accès aux fichiers sur un disque
  • Logique dans une application monopage qui attend les modifications apportées à l'emplacement actuel, puis crée et affiche un élément DOM correspondant.

Bien qu'il n'existe pas de norme définitive, les développeurs Web ont tendance à utiliser une syntaxe commune pour exprimer les modèles de routage d'URL qui partagent beaucoup de points communs avec regular expressions, mais avec quelques ajouts spécifiques au domaine, comme des jetons pour faire correspondre les segments de chemin d'accès. Les frameworks côté serveur populaires tels que Express et Ruby on Rails utilisent cette syntaxe (ou une syntaxe 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 s'ajoute à la plate-forme Web et s'appuie sur les bases créées par ces frameworks. Son objectif est de standardiser 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 jetons et de groupes.

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

Compatibilité avec les navigateurs et polyfills

URLPattern est activé par défaut dans Chrome et Edge version 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. 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'ont 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

L'une des philosophies de URLPattern est d'éviter la réinvention. Si vous connaissez déjà la syntaxe de routage utilisée dans Express ou Ruby on Rails, vous n'aurez rien de nouveau à apprendre. Toutefois, compte tenu des 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 donc 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 consultation approfondie avec le responsable actuel de path-to-regexp.

Pour vous familiariser avec la syntaxe de base acceptée, le mieux est de consulter la documentation de 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 prend en charge, car URLPattern prend en charge une fonctionnalité peu courante parmi les bibliothèques de routage : la correspondance des 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 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 même origine dans une application Web autonome.

La prise en compte des origines ouvre la voie à d'autres cas d'utilisation, comme le routage des requêtes cross-origin à l'intérieur du gestionnaire d'événements fetch d'un service worker. Si vous ne routez 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 à comparer.

Transmettre un objet offre le contrôle le plus explicite sur le modèle à utiliser pour faire correspondre chaque composant d'URL. Dans sa version 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é, la correspondance ne se fera 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é, équivaut à les définir sur le caractère générique '*'. L'exemple pourrait être simplifié comme suit :

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

Pour faire encore plus court, 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 supposent que votre cas d'utilisation implique des origines correspondantes. Si vous ne souhaitez faire correspondre que les autres parties de l'URL, à l'exclusion de l'origine (comme c'est le cas pour de nombreux scénarios de routage à origine unique), vous pouvez omettre complètement les informations sur l'origine et ne fournir qu'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 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 modèle d'URL complet, y compris les informations de modèle utilisées pour faire correspondre l'origine. Si vous fournissez deux chaînes, la seconde est utilisée comme baseURL et la première est considérée comme relative à cette base.

Qu'une ou deux chaînes soient fournies, le constructeur URLPattern analysera le modèle d'URL complet, le décomposera en composants d'URL et mappera chaque partie du modèle plus grand au composant correspondant. Cela signifie qu'en interne, 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 verbeuse.

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

Lorsque vous utilisez des chaînes pour créer un URLPattern, vous devez tenir compte de quelques mises en garde.

Si vous omettez une propriété lorsque vous utilisez un objet pour construire URLPattern, cela équivaut à fournir un caractère générique * pour cette propriété. Lorsque le format de chaîne de l'URL complète est analysé, si l'un des composants de l'URL ne comporte pas de valeur, il est traité comme si la propriété du composant était définie sur '', ce qui ne correspondra que lorsque ce composant 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',
});

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

Utiliser le modèle

Une fois que vous avez créé un URLPattern, vous avez deux options pour l'utiliser. Les méthodes test() et exec() utilisent toutes deux les mêmes entrées et le même algorithme pour vérifier la correspondance. Elles ne diffèrent que par leur valeur de retour. 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 montrent comment utiliser exec(), mais vous pouvez remplacer test() par n'importe lequel d'entre eux si vous ne souhaitez qu'une valeur booléenne.

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, y compris 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 celui accepté par 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 donne des informations sur les valeurs correspondantes dans l'URL d'entrée. Vous n'aurez ainsi pas à analyser vous-même ces valeurs.

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 comporte des propriétés qui correspondent aux composants de URLPattern, comme pathname. Ainsi, si un groupe a été défini dans la partie pathname de URLPattern, les correspondances peuvent être trouvées dans 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 modèle 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 les index suivants 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é et normalisation Unicode

URLPattern est compatible avec les caractères Unicode de plusieurs façons.

  • 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 sera automatiquement encodé selon les mêmes règles que celles utilisées pour l'encodage d'URL de ce composant spécifique. Les caractères Unicode dans pathname seront encodés en pourcentage. Par conséquent, un modèle pathname tel que /café sera automatiquement normalisé en /caf%C3%A9. Les caractères Unicode dans hostname sont automatiquement encodés à l'aide de Punycode, plutôt que de l'encodage 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 manuellement en pourcentage, comme (caf%C3%A9) pour faire correspondre café.

En plus d'encoder les caractères Unicode, URLPattern normalise également les URL. Par exemple, /foo/./bar dans le composant pathname est réduit à l'équivalent /foo/bar.

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.

Regrouper tous les éléments

La démo Glitch 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 qui pourraient 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

Bien que les fonctionnalités de base de URLPattern soient disponibles dans Chrome et Edge, d'autres sont prévues. Certains aspects de URLPattern sont encore en développement, et il existe un certain nombre de questions ouvertes concernant des comportements spécifiques qui peuvent encore être affinés. Nous vous encourageons à essayer URLPattern et à nous faire part de vos commentaires en signalant un problème sur 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 des jetons, et renvoie une chaîne pour un chemin d'URL avec ces valeurs substituées.

Nous espérons ajouter cela à URLPattern à l'avenir, mais ce n'est 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 qui pourraient bénéficier du routage ou de la correspondance de modèles peuvent s'appuyer dessus en tant que primitive.

Des discussions sont en cours concernant l'utilisation de URLPattern pour les fonctionnalités proposées, telles que la correspondance des modèles de portée des service workers, les PWA en tant que gestionnaires de fichiers et la prérécupération spéculative.

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