Quelle est la différence entre URI.escape et CGI.escape?

147

Quelle est la différence entre URI.escapeet CGI.escapeet lequel dois-je utiliser?

Tom Lehman
la source

Réponses:

124

Il y avait quelques petites différences, mais le point important est qu'il URI.escapeest obsolète dans Ruby 1.9.2 ... alors utilisez CGI::escapeou ERB :: Util.url_encode .

Il y a une longue discussion sur ruby-core pour les personnes intéressées qui mentionne également WEBrick :: HTTPUtils.escape et WEBrick :: HTTPUtils.escape_form .

Marc-André Lafortune
la source
11
Juste pour ajouter à la confusion - je viens de voir un commentaire sur stackoverflow.com/questions/4967608/… où quelqu'un a mentionné que cgi escape utilise '+' au lieu de% 20 pour les espaces, et que c'est contre la 'spec' ...
Louis Sayers
18
une alternative est de l'utiliser ERB::Util.url_encodecorrectement %20 pour les espaces
riffraff
1
@Ernest: Voir: github.com/ruby/ruby/commit/… (réponse mise à jour)
Marc-André Lafortune
4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html . Il existe un module URI.escape dans ruby ​​2.0.0. Pourquoi était-il obsolète?
user938363
1
@ user938363 si vous cliquez sur la source de l'émission, vous verrez qu'elle est toujours marquée comme obsolète.
tirage le
229

Quelle est la différence entre une hache et une épée et laquelle dois-je utiliser? Eh bien, cela dépend de ce que vous devez faire.

URI.escapeétait censé encoder une chaîne (URL) dans, soi-disant, " encodage en pourcentage ".

CGI::escapeprovient de la spécification CGI , qui décrit comment les données doivent être encodées / décodées entre le serveur Web et l'application.

Maintenant, disons que vous devez échapper à un URI dans votre application. C'est un cas d'utilisation plus spécifique. Pour cela, la communauté Ruby l'utilise URI.escapedepuis des années. Le problème avec URI.escapeétait qu'il ne pouvait pas gérer la spécification RFC-3896.

URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog' 
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog"

URI.escape a été marqué comme obsolète:

De plus, URI.encode actuel est un simple gsub. Mais je pense qu'il devrait diviser un URI en composants, puis échapper à chaque composant et enfin les joindre.

Donc, URI.encode actuel est considéré comme dangereux et obsolète. Cela sera supprimé ou changera radicalement de comportement.

Quel est le remplacement en ce moment?

Comme je l'ai dit ci-dessus, le code URI.encode actuel est incorrect au niveau des spécifications. Nous ne fournirons donc pas le remplacement exact. Le remplacement variera selon son cas d'utilisation.

https://bugs.ruby-lang.org/issues/4167

Malheureusement, il n'y a pas un seul mot à ce sujet dans la documentation, la seule façon de le savoir est de vérifier la source, ou d'exécuter le script avec des avertissements au niveau détaillé ( -wW2) (ou d'utiliser un google-fu).

Certains ont proposé d'utiliser CGI::Escapepour les paramètres de requête, car vous ne pouviez pas échapper à un URI entier:

CGI::escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http%3A%2F%2Fgoogle.com%2Ffoo%3Fbar%3Dat%23anchor%26title%3DMy+Blog+%26+Your+Blog"

CGI::escapene doit être utilisé que pour les paramètres de requête, mais les résultats seront, encore une fois, contre les spécifications. En fait, le cas d'utilisation le plus courant est l'échappement des données du formulaire, par exemple lors de l'envoi d'une application/x-www-form-urlencodedrequête POST.

Il WEBrick::HTTPUtils.escapeest également mentionné qu'il n'y a pas beaucoup d'amélioration (encore une fois, c'est juste une simple gsub, qui est, l'OMI, une option encore pire que URI.escape):

WEBrick::HTTPUtils.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog" 

Le plus proche de la spécification semble être le joyau adressable :

require 'addressable/uri'
Addressable::URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at#anchor&title=My%20Blog%20&%20Your%20Blog"

Notez que contrairement à toutes les options précédentes, Addressable ne s'échappe pas #, et c'est le comportement attendu. vous souhaitez conserver le #hachage dans le chemin URI mais pas dans la requête URI.

Le seul problème qui reste est que nous n'avons pas échappé correctement à nos paramètres de requête, ce qui nous amène à la conclusion: nous ne devrions pas utiliser une seule méthode pour l'URI entier, car il n'y a pas de solution parfaite (jusqu'à présent). Comme vous le voyez, &n'a pas été échappé de "Mon blog et votre blog". Nous devons utiliser une autre forme d'échappement pour les paramètres de requête, où les utilisateurs peuvent mettre différents caractères qui ont une signification particulière dans les URL. Entrez le code URL. L'encodage d'URL doit être utilisé pour chaque valeur de requête "suspecte", de la même manière que ERB::Util.url_encode:

ERB::Util.url_encode "My Blod & Your Blog"
# => "My%20Blod%20%26%20Your%20Blog""

C'est cool mais nous avons déjà requis adressable:

uri = Addressable::URI.parse("http://www.go.com/foo")
# => #<Addressable::URI:0x186feb0 URI:http://www.go.com/foo>
uri.query_values = {title: "My Blog & Your Blog"}
uri.normalize.to_s
# => "http://www.go.com/foo?title=My%20Blog%20%26%20Your%20Blog"

Conclusion:

  • Ne pas utiliser URI.escapeou similaire
  • À utiliser CGI::escapesi vous n'avez besoin que d'un échappement de formulaire
  • Si vous devez travailler avec des URI, utilisez Addressable, il offre un encodage d'URL, un encodage de formulaire et normalise les URL.
  • S'il s'agit d'un projet Rails, consultez " Comment puis-je échapper une URL à une chaîne dans Rails? "
Ernest
la source
Merci beaucoup pour l'info. Il s'est débarrassé de certains avertissements de test de houe. Un râteau et une houe regardent ci-dessous.
Douglas
Excellente explication @Ernest, mais le problème avec ceci est que cela ne fonctionnera pas pour les URL externes que je n'essaye pas de créer (et sur lesquelles je n'ai aucun contrôle). par exemple, les robots d'exploration qui lisent les URL d'une page Web, puis essaient d'accéder à ces URL (qui doivent être codées avant l'accès).
amit_saxena
@amit_saxena si vous pouvez vous permettre d'avoir Addressablecomme l'un de vos joyaux, vous pouvez d'abord analyser l'URL, fi rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ernest
Intéressant! Mais encore une fois, je ne peux pas obtenir un hachage des paramètres de l'url d'origine en utilisant ceci, que j'encode ensuite comme vous le décrivez. Le flux dans mon cas est le suivant: j'obtiens des URL externes à partir d'un flux -> que je dois ensuite encoder -> Passer au client http pour récupérer le contenu. Maintenant, si je n'encode pas correctement les URL externes, les clients HTTP basés sur ruby ​​échouent avec des erreurs d'URI non valides.
amit_saxena
La méthode d'analyse @amit_saxena retournera l'instance de Addressable:URL, vous pouvez alors appeler toutes les méthodes d'instance dessus, peut-être que l'une d'elles obtiendra les résultats souhaités: rubydoc.info/gems/addressable/Addressable/URI
Ernest
6

CGI::escapeest bon pour échapper des segments de texte afin qu'ils puissent être utilisés dans les paramètres de requête d'URL (chaînes après '?'). Par exemple, si vous voulez avoir un paramètre contenant des barres obliques dans l'url, vous CGI :: échappez d'abord cette chaîne, puis insérez-la dans l'url.

Cependant, dans Rails, vous ne l'utiliserez probablement pas directement. Habituellement, vous utilisez hash.to_param, qui utilisera CGI::escapesous le capot.


URI::escapeest bon pour échapper à une URL qui n'a pas été correctement échappée. Par exemple, certains sites Web affichent une URL incorrecte / non échappée dans leur balise d'ancrage. Si votre programme utilise ces URL pour récupérer plus de ressources, OpenURI se plaindra que les URL ne sont pas valides. Vous en avez besoin pour URI::escapeen faire une URL valide. Il est donc utilisé pour échapper à toute la chaîne URI pour la rendre correcte. Dans mon mot, URI :: unescape rend une URL lisible par l'homme, et URI :: escape la rend valide pour les navigateurs.

Ce sont les termes de mon profane et n'hésitez pas à les corriger.

lulalala
la source
1

La différence est que URI.escape ne fonctionne pas ...

CGI.escape"/en/test?asd=qwe"
=> "%2Fen%2Ftest%3Fasd%3Dqwe"

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"
Radu Simionescu
la source
2
Vous avez choisi le mauvais scénario de test. Les /, les? Et les = font tous partie d'un URI valide et ne sont donc pas échappés. Les autres caractères qui doivent être échappés, en particulier dans la chaîne de requête, devraient l'être.
Gerard ONeill
@GerardONeill J'ai choisi le cas de test précisément pour montrer comment URI.escape ne fonctionne pas et n'est pas fiable. Suggérez-vous que URI.escape échappe uniquement la chaîne de requête? comment pourrait-il savoir quand une valeur de paramètre est terminée si je veux encoder un & dedans? c'est peut-être pour cela qu'il est obsolète?
Radu Simionescu
1
C'est exactement ce que je dis. L'échappement URI doit analyser l'URL, séparer ce qu'il pense être les paramètres individuels, les échapper et les remettre ensemble. Même cela peut être compliqué. Mais cela ne fait pas cela - cela évite simplement d'échapper à certains caractères tout en échappant au reste, ce qui le rend incomplet. Il peut être utilisé pour des cas simples, surtout si vous savez que vos paramètres ne seront pas ... déroutants.
Gerard ONeill
0

CGI.escape sert à échapper une valeur URL dans la chaîne de requête. Tous les caractères qui ne tombent pas dans ALPHA, DIGIT, '_', '-', '.' et le jeu de caractères «» est échappé.

Mais cela rendrait une URL incorrecte, car une URL doit avoir "/", ":", "?", "[", "&", "=" Et ";". Peut-être plus que je ne peux pas penser du haut de ma tête.

URI.escape laisse ces caractères URL seuls et essaie de trouver les clés de chaîne de requête et les valeurs à échapper. Cependant, cela ne peut vraiment pas être dépendant car les valeurs peuvent avoir toutes sortes de caractères empêchant une évasion facile. En gros, il est trop tard. Mais si l'URL peut être considérée comme simple (pas de «&» et «=», etc. dans les valeurs), cette fonction peut être utilisée pour échapper des caractères peut-être illisibles ou illégaux.

En général, utilisez toujours CGI.escape sur les clés et valeurs individuelles avant de les joindre avec «&» et de les ajouter après le «?».

Gérard ONeill
la source
0

CGI.escape ne fonctionnait pas avec l'API OpenProject. Il encodait le [] ,: et non le +. J'ai piraté cela ensemble, ce qui semble fonctionner jusqu'à présent pour l'API d'OpenProject. Mais je suis sûr qu'il manque quelques .gsub. C'est probablement presque aussi mauvais que URI.escape, mais cela ne vous donnera pas les erreurs obsolètes.

class XXX
      def self.encode(path)
        path, query = path.split("?", 2)
        return path if query.nil?
        query = CGI.escape(query).gsub("%3A", ":").gsub("%3D","=").gsub("%5B","[").gsub("%5D","]").gsub("%2C",",").gsub("+","%20")
        return [path,query].join("?")
      end
end

XXX.encode("http://test.com/some/path?query=[box: \"cart\"]")
URI.encode("http://test.com/some/path?query=[box: \"cart\"]")

Les deux sorties:

=> " http://test.com/some/path?query=[box:%20%22cart%22] "
=> " http://test.com/some/path?query=[box:%20 % 22cart% 22] "

Brett
la source