Comment encoder une URL dans Ruby

135

Comment puis-je URI::encodeune chaîne comme:

\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a

pour l'obtenir dans un format comme:

%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A

selon RFC 1738?

Voici ce que j'ai essayé:

irb(main):123:0> URI::encode "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a"
ArgumentError: invalid byte sequence in UTF-8
    from /usr/local/lib/ruby/1.9.1/uri/common.rb:219:in `gsub'
    from /usr/local/lib/ruby/1.9.1/uri/common.rb:219:in `escape'
    from /usr/local/lib/ruby/1.9.1/uri/common.rb:505:in `escape'
    from (irb):123
    from /usr/local/bin/irb:12:in `<main>'

Aussi:

irb(main):126:0> CGI::escape "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a"
ArgumentError: invalid byte sequence in UTF-8
    from /usr/local/lib/ruby/1.9.1/cgi/util.rb:7:in `gsub'
    from /usr/local/lib/ruby/1.9.1/cgi/util.rb:7:in `escape'
    from (irb):126
    from /usr/local/bin/irb:12:in `<main>'

J'ai tout regardé sur Internet et je n'ai pas trouvé de moyen de le faire, même si je suis presque certain que l'autre jour, j'ai fait cela sans aucun problème.

HRÓÐÓLFR
la source
1
Peut-être utile si vous utilisez Ruby 1.9: yehudakatz.com/2010/05/05/…
apneadiving le

Réponses:

179
str = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a".force_encoding('ASCII-8BIT')
puts CGI.escape str


=> "%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A"
kain
la source
2
force_encoding('binary')pourrait être un choix plus auto-documenté.
mu est trop court
63
Ils ont déconseillé cette méthode, utilisez CGI.escapeplutôt * *. -> http://www.ruby-forum.com/topic/207489#903709 . Vous devriez également pouvoir utiliser URI.www_form_encode* URI.www_form_encode_component*, mais je ne les ai jamais utilisés
J-Rou
2
Pas besoin d' require 'open-uri'ici. Voulez-vous dire require 'uri'?
pje
1
@ J-Rou, CGI.escape peut échapper à toute l'URL, il n'échappe pas sélectivement aux paramètres de requête, par exemple, si vous passez 'a=&!@&b=&$^'à CGI.escape, il échappera à tout avec des séparateurs de requête &afin que cela ne puisse être utilisé que pour interroger des valeurs. Je suggère d'utiliser addressablegem, c'est plus intellectuel de travailler avec des URL.
Alexander.Iljushkin
J'avais besoin d'accéder aux fichiers sur un serveur distant. L'encodage avec CGI ne fonctionnait pas, mais URI.encode faisait très bien le travail.
Tashows
82

De nos jours, vous devez utiliser ERB::Util.url_encodeou CGI.escape. La principale différence entre eux est leur gestion des espaces:

>> ERB::Util.url_encode("foo/bar? baz&")
=> "foo%2Fbar%3F%20baz%26"

>> CGI.escape("foo/bar? baz&")
=> "foo%2Fbar%3F+baz%26"

CGI.escapesuit la spécification des formulaires CGI / HTML et vous donne une application/x-www-form-urlencodedchaîne, qui exige que les espaces soient échappés +, alors que ERB::Util.url_encodesuit la RFC 3986 , qui exige qu'ils soient encodés comme %20.

Voir « Quelle est la différence entre URI.escape et CGI.escape? » Pour plus de détails.

Jenner La Fave
la source
70
str = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a"
require 'cgi'
CGI.escape(str)
# => "%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A"

Tiré du commentaire de @ J-Rou

Jared Beck
la source
11

Vous pouvez utiliser des Addressable::URIgemmes pour cela:

require 'addressable/uri'   
string = '\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a'
Addressable::URI.encode_component(string, Addressable::URI::CharacterClasses::QUERY)
# "%5Cx12%5Cx34%5Cx56%5Cx78%5Cx9a%5Cxbc%5Cxde%5Cxf1%5Cx23%5Cx45%5Cx67%5Cx89%5Cxab%5Cxcd%5Cxef%5Cx12%5Cx34%5Cx56%5Cx78%5Cx9a" 

Il utilise un format plus moderne que CGI.escape, par exemple, il encode correctement l'espace en tant %20que +signe et non en tant que signe, vous pouvez en savoir plus dans " Le type application / x-www-form-urlencoded " sur Wikipedia.

2.1.2 :008 > CGI.escape('Hello, this is me')
 => "Hello%2C+this+is+me" 
2.1.2 :009 > Addressable::URI.encode_component('Hello, this is me', Addressable::URI::CharacterClasses::QUERY)
 => "Hello,%20this%20is%20me" 
Alexey Shein
la source
Peut également faire comme ceci: CGI.escape('Hello, this is me').gsub("+", "%20") => Hello%2C%20this%20is%20me"si vous ne voulez pas utiliser de gemmes
Raton laveur
5

J'ai créé un joyau pour rendre le codage URI plus propre à utiliser dans votre code. Il s'occupe de l'encodage binaire pour vous.

Exécutez gem install uri-handler, puis utilisez:

require 'uri-handler'

str = "\x12\x34\x56\x78\x9a\xbc\xde\xf1\x23\x45\x67\x89\xab\xcd\xef\x12\x34\x56\x78\x9a".to_uri
# => "%124Vx%9A%BC%DE%F1%23Eg%89%AB%CD%EF%124Vx%9A"

Il ajoute la fonctionnalité de conversion URI dans la classe String. Vous pouvez également lui passer un argument avec la chaîne de codage facultative que vous souhaitez utiliser. Par défaut, il est défini sur le codage «binaire» si le codage UTF-8 simple échoue.

foomip
la source
2

Code:

str = "http://localhost/with spaces and spaces"
encoded = URI::encode(str)
puts encoded

Résultat:

http://localhost/with%20spaces%20and%20spaces
Thiago Falcao
la source
Si le serveur de réception est ancien, il risque de ne pas répondre correctement à CGI.escape. C'est toujours une alternative valable.
cesartalves le
2

J'essayais à l'origine d'échapper aux caractères spéciaux dans un nom de fichier uniquement, pas sur le chemin, à partir d'une chaîne URL complète.

ERB::Util.url_encode n'a pas fonctionné pour mon utilisation:

helper.send(:url_encode, "http://example.com/?a=\11\15")
# => "http%3A%2F%2Fexample.com%2F%3Fa%3D%09%0D"

Basé sur deux réponses dans " Pourquoi URI.escape () est-il marqué comme obsolète et où est cette constante REGEXP :: UNSAFE? ", Il semble que URI::RFC2396_Parser#escapec'est mieux que d'utiliser URI::Escape#escape. Cependant, ils se comportent tous les deux de la même manière:

URI.escape("http://example.com/?a=\11\15")
# => "http://example.com/?a=%09%0D"
URI::Parser.new.escape("http://example.com/?a=\11\15")
# => "http://example.com/?a=%09%0D"
kangkyu
la source
2

Si vous voulez "encoder" une URL complète sans avoir à penser à la diviser manuellement en ses différentes parties, j'ai trouvé que ce qui suit fonctionnait de la même manière que j'utilisais URI.encode:

URI.parse(my_url).to_s
Glenn 'devalias'
la source