Français

Outils de développement · Encodeur et décodeur d'URL

escape() vs encodeURIComponent : comment l'encodage d'URL de JavaScript a évolué

· Contexte

javascript encodage d'URL historique

Trois générations de fonctions d'encodage d'URL JavaScript
Illustration vectorielle originale de ToolAcre

JavaScript a eu trois générations de fonctions de codage d'URL, et la plus ancienne se cache toujours dans le code de production. Cet article explique ce que escape() fait de mal, pourquoi ES3 a ajouté les fonctions URI et pourquoi elles préservent ! *' ( ).

Le %u20AC dans un ancien journal – l'empreinte digitale indubitable de escape() et l'échec de décodage qu'il provoque

Un ancien fichier JavaScript contient un appel de codage d'URL utilisant la fonction obsolète escape(). La sortie dans un fichier journal ou un message d'erreur inclut la séquence %u20AC, une empreinte digitale indubitable de la fonction obsolète escape() que personne d'autre n'utilise. Cette séquence ne correspond à aucun codage d'URL standard, et un décodeur construit sur les règles RFC 3986 ou WHATWG ne la reconnaîtra pas. Les données ne peuvent pas faire l’aller-retour via les outils modernes. Il s'agit d'un signe de code courant antérieur à ES3 et qui n'a pas été mis à jour depuis les années 1990.

La fonction escape() a été conçue à l'époque de Netscape, avant que JavaScript ait des standards ou des règles formelles de codage d'URL. Il code la plupart des caractères non-ASCII en utilisant la notation %uXXXX, un code hexadécimal à quatre chiffres que personne d'autre n'utilise et qu'aucune norme ne définit nulle part. Cela était logique pour des utilisations ponctuelles dans un navigateur, mais cela rompait la compatibilité avec les normes d'URL et rendait les données impossibles à décoder ailleurs.

escape() et unescape() : une conception de l'ère Netscape — hypothèses latines-1, l'invention %uXXXX et pourquoi elle n'a jamais correspondu à aucune norme

escape() et unescape() supposent que l'entrée est Latin-1 (ISO 8859-1), le codage de caractères antérieur à UTF-8 et Unicode. Ils convertissent chaque caractère en code hexadécimal, en utilisant %XX pour les caractères Latin-1 à bits élevés et %uXXXX pour tout ce qui n'est pas Latin-1. Un caractère non latin-1 comme un emoji ne peut pas du tout être représenté. Les fonctions sont simples et rapides, mais elles sont également totalement inappropriées pour tout cas d'utilisation moderne.

Les deux fonctions ont été ajoutées à JavaScript avant l'existence des normes. Ils ont été obsolètes immédiatement après qu'ES3 ait introduit le codage d'URL approprié dans 1999. Ils restent en JavaScript pour des raisons de compatibilité descendante : les supprimer briserait le code ancien. Mais tout nouveau code ne devrait jamais les utiliser. Ils sont une relique de l'héritage.

ES3 (1999) ajoute encodeURI et encodeURIComponent — UTF-8 pourcentage de codage aligné avec RFC 2396

ES3 a introduit deux fonctions : encodeURI et encodeURIComponent. Les deux effectuent un codage en pourcentage UTF-8 : convertissez les caractères non-ASCII en UTF-8 octets, puis écrivez chaque octet sous la forme %HH. Les deux s'alignent sur la RFC 2396, qui était en vigueur à l'époque. La RFC 3986 est arrivée plus tard et n'a pas modifié le comportement de codage. Ces fonctions sont encore aujourd’hui la norme et doivent être utilisées.

encodeURI est destiné à coder un URI complet ; encodeURIComponent est destiné à coder un composant à l'intérieur d'un URI, comme une valeur de requête ou un segment de chemin. La différence est absolument cruciale et facile à mal comprendre. encodeURI préserve les caractères structurels tels que : /? # @ = & et ;. encodeURIComponent code tous ces éléments, ce qui permet de les intégrer en toute sécurité dans un URI plus grand.

Pourquoi ! * ' ( ) ne sont toujours pas codés — les caractères de « marque » RFC 2396 sont figés dans la langue après que RFC 3986 les a déplacés.

Les deux fonctions laissent ces caractères non codés : lettres, chiffres, trait d'union (-), trait de soulignement (_), point (.), tilde (~) et les cinq signes de ponctuation ! *' ( ). Les marques provenaient de la RFC 2396, qui les répertoriait comme caractères de « marque » non réservés. La RFC 3986 est sortie dans 2005 et a déplacé ces cinq éléments dans une catégorie différente, mais JavaScript avait déjà gelé encodeURI et encodeURIComponent dans 1999. Changer les personnages qu'ils laissent seuls briserait le code existant, alors ils sont restés.

La décision de conserver ces cinq marques non codées pour des raisons de compatibilité descendante signifie que le codage de JavaScript ne correspond parfaitement ni à la RFC 3986, ni à la norme WHATWG. Il est suffisamment proche pour une utilisation pratique et il est désormais totalement impossible de le modifier. C’est une leçon de stabilité des API : une fois que l’on fige un comportement, on ne peut plus le changer même si le standard évolue.

Exemple fonctionnel : la même chaîne via escape, encodeURI et encodeURIComponent – trois sorties comparées

Prenez la chaîne "R&D (research) = café's". Exécutez-le via escape(), encodeURI et encodeURIComponent. escape() produit "R%26D%20(research)%20%3D%20caf%E9's", mélangeant des parenthèses et des apostrophes non codées avec percent-encoded esperluette et égaux. encodeURI produit "R&D%20(research)%20=%20caf%C3%A9's", laissant les signes esperluette et égal seuls car ils sont structurels. encodeURIComponent produit "R%26D%20%28research%29%20%3D%20caf%C3%A9%27s", codant tout, y compris les parenthèses et l'apostrophe.

Collez la même chaîne dans l'encodeur et le décodeur d'URL et basculez entre encodeURI et encodeURIComponent pour voir la différence. Inspectez ensuite ce que escape() produirait (vous pouvez l'appeler dans la console du navigateur, même s'il vous avertira). Vous voyez immédiatement que les trois fonctions produisent trois résultats complètement différents.

Migrer depuis escape() - mapper les anciens appels vers la bonne fonction moderne et gérer les données %uXXXX stockées

L'ancien code utilisant escape() doit être mis à jour. Si escape() a été utilisé pour coder un composant URI, remplacez-le par encodeURIComponent. S'il a été utilisé pour coder un URI complet, utilisez encodeURI. Pour les données stockées contenant des séquences %uXXXX, vous avez besoin d'un décodeur personnalisé : convertissez chaque %uXXXX en un point de code Unicode, puis collectez les points de code dans une chaîne. built-in unescape() de JavaScript lira le %uXXXX, mais le résultat pourrait ne pas être correct UTF-8.

Après avoir remplacé escape(), testez le code avec des chaînes contenant des caractères non-ASCII, des signes de ponctuation et des caractères spéciaux. Le résultat devrait désormais correspondre à ce qu’attendent les outils et normes modernes. Si votre code est antérieur à ES3 de manière significative, il peut également utiliser d'autres modèles obsolètes ; un audit complet en vaut la peine.

Ce que cela ne couvre pas : les API URL et URLSearchParams, qui sont couvertes séparément

Les API URL et URLSearchParams, ajoutées bien plus tard, fournissent des interfaces de niveau supérieur pour la construction d'URL et l'encodage de composants. Ils gèrent automatiquement tous les échappements et correspondent exactement à la norme d'URL WHATWG. Il s'agit du moyen privilégié pour créer des URL par programmation en JavaScript moderne.

Cet article couvre uniquement les fonctions d'encodage, pas les API de niveau supérieur. URL et URLSearchParams analysent la structure, sélectionnent les règles des composants et sérialisent le résultat, tandis que encodeURIComponent transforme une chaîne fournie sans savoir où elle sera placée. Cette distinction est la limite : migrez un ancien appel escape() selon qu'il gère une valeur ou une adresse, puis envisagez de remplacer la concaténation manuelle environnante par les API structurées en tant que refactor distinct.

À retenir : trois fonctions, une paire survivante – comment l'encodeur et le décodeur d'URL affichent côte à côte le comportement moderne d'encodeURI et d'encodeURIComponent.

Le développement JavaScript moderne doit utiliser encodeURI ou encodeURIComponent, jamais escape(). Les fonctions ont été standardisées dans 1999 et n'ont pas changé depuis. Ils codent les caractères non-ASCII sous forme de UTF-8 octets et gèrent correctement les caractères réservés standard. L'outil d'encodeur et de décodage d'URL implémente les deux fonctions et vous permet de voir leur comportement côte à côte, ce qui facilite le choix de celle qui convient à votre composant.

Si vous rencontrez des séquences %u dans d'anciens journaux ou des données stockées, elles sont une sortie escape() et doivent être migrées. La migration est simple une fois que vous avez identifié le modèle. Le code moderne ne devrait jamais les produire.