Encodage des urls
Par amethyste le Fév 9, 2009 | Dans focus | Réagir »
Un récent article de Rick Strahl me rappelle un problème rencontré sur un projet.
J'avais la charge de l'écriture d'un frontal qui consolidait des résultats reçus de divers services Web. Nous avons passé pas mal de temps à nous bagarrer contre des problèmes de codage/décodage incompatibles entre eux et qui apparaissaient de façon en apparence aléatoire…
Que se passait-il?
Pour diverses raisons ces services pouvaient (ou non) encoder certaines données de différentes façons. De mon côté je devais donc faire l'opération inverse pour afficher correctement le résultat et permettre aux utilisateurs d'éventuellement les modifier. Oui, mais laquelle, selon quels critères? Bref on s'est pas mal amusé…
L'espace de noms System.Web contient les méthodes d'encodages suivantes:
1. UrlEncoding
2. UrlDecoding
3. HtmlEncoding
4. HtmlDecoding
Decoding défaisant ce qu'Encoding fait.
On retrouve également UrlEncoding/Decoding dans System.
La question que l'on peut se poser est a quoi servent autant de méthodes. Voici un exemple adapté du papier de Rick Strahl qui devrait nous servir de base de discussion:
string test = "This is a value & I don't care for it.\t\"quoted\" 'single quoted',<% alligator %>#"; string UrlHttpUtility_Encoded = System.Web.HttpUtility.UrlEncode(test); string UriDataEscaped_Encoded = System.Uri.EscapeDataString(test); string UriEscaped_Encoded = System.Uri.EscapeUriString(test);
Le résultat est résumé ici ans l'ordre où ils sont générés:
This+is+a+value+%26+I+don't+care+for+it.%09%22quoted%22+'single+quoted'%2c%3c%25+alligator+%25%3e%23 This%20is%20a%20value%20%26%20I%20don't%20care%20for%20it.%09%22quoted%22%20'single%20quoted'%2C%3C%25%20alligator%20%25%3E%23 This%20is%20a%20value%20&%20I%20don't%20care%20for%20it.%09%22quoted%22%20'single%20quoted',%3C%25%20alligator%20%25%3E#
Ces méthodes disposent d'une méthode de décodage. Euuh, en fait pas toutes. Par exemple je n'ai rien trouvé pour EscapeUriString.
Il se pose donc la question de la compatibilité de ces méthodes entre elles, ce que nous pouvons tester derechef! (derechef: on m'aurait dit que je caserais un jour ce mot…)
string s1 = System.Uri.UnescapeDataString(UrlHttpUtility_Encoded); string s2 = System.Web.HttpUtility.UrlDecode(UrlHttpUtility_Encoded); string s3 = System.Web.HttpUtility.UrlDecode(UriDataEscaped_Encoded); string s4 = System.Uri.UnescapeDataString(UriDataEscaped_Encoded); string s5 = System.Web.HttpUtility.UrlDecode(UriEscaped_Encoded); string s6 = System.Uri.UnescapeDataString(UriEscaped_Encoded);
On obtient alors:

Soit 5 résultats corrects sur 6, ce n'est pas si mal! Et surtout, remarquez que la variante Decode sait traiter correctement sa sœur en Encode.
La conclusion est qu'il est peut être préférable de ne pas encoder ses url avec System.Web.HttpUtility.UrlEncode, si on peut l'éviter, à moins d'être certains d'employer la bonne méthode de décodage. Ce qui suppose de connaître à l'avance la méthode d'encodage et c'était justement un de nos problèmes…
Et sur le fond, c'est une assez bonne nouvelle comme le souligne Rick Strahl. En effet, on n'a alors plus besoin de référencer System.Web qui pèse tout de même 2,5 Mo dans une application non Web pour faire une malheureuse opération d'encodage/décodage d'url.
Pour encoder on utilise EscapeDataString(), mais pour décoder:
public static string UrlDecode(string text) { // au cas où l'encodage a été fait avec // System.Web.HttpUtility.UrlEncode() text = text.Replace("+", " "); return System.Uri.UnescapeDataString(text); }
Oui, il faut tout de même prévoir le cas où l'url a été encodée avec System.Web. On a donc une méthode universelle de décodage qui ne passe pas par System.Web. Rick Strahl montre aussi une fonction qui sait récupérer une donnée dans une chaîne de requête encodée.
Pour éliminer la référence à System.Web il reste encore à traiter le cas de l'encodage HTML. La solution proposée est la suivante:
public static string HtmlEncode(string text) { if (text == null) { return null; } StringBuilder sb = new StringBuilder(text.Length); int len = text.Length; for (int i = 0; i < len; i++) { switch (text[i]) { case '<': sb.Append("<"); break; case '>': sb.Append(">"); break; case '"': sb.Append("""); break; case '&': sb.Append("&"); break; default: if (text[i] > 159) { // decimal numeric entity sb.Append("&#"); sb.Append(((int)text[i]).ToString(CultureInfo.InvariantCulture)); sb.Append(";"); } else { sb.Append(text[i]); } break; } } return sb.ToString(); }
La principale différence entre cette solution et l'implémentation proposée par Microsoft est qu'à Redmond ils encodent avec des entités uniquement sur la plage [160-255] et non pas tout ce qui est au dessus de 159. Si quelqu'un a une explication…
Bibliographie
Article de Rick Strahl:
http://www.west-wind.com/weblog/posts/617930.aspx
Message subliminal à Sami:
C'est quand que tu nous installe la version suivante de B2Evolution que tu nous a fais miroiter il y a un an?
Aucun commentaire pour le moment
Laisser un commentaire
| « Lab sur le bloc d'application Validation | Trucs en vrac pour démarrage en douceur » |