¿Cuál es la diferencia entre URI.escape y CGI.escape?

147

¿Cuál es la diferencia entre URI.escapey CGI.escapey cuál debería usar?

Tom Lehman
fuente

Respuestas:

124

Hubo algunas pequeñas diferencias, pero el punto importante es que URI.escapeha quedado en desuso en Ruby 1.9.2 ... así que use CGI::escapeo ERB :: Util.url_encode .

Hay una larga discusión sobre ruby-core para aquellos interesados ​​que también menciona WEBrick :: HTTPUtils.escape y WEBrick :: HTTPUtils.escape_form .

Marc-André Lafortune
fuente
11
Sólo para añadir a la confusión - Acabo de ver un comentario en stackoverflow.com/questions/4967608/... cuando alguien mencionó que cgi de escape usos '+' en lugar de 20% para los espacios, y que va en contra de la 'spec' ...
Louis Sayers
18
Una alternativa es usar los espacios ERB::Util.url_encodecorrectamente%20
riffraff
1
@Ernest: Ver: github.com/ruby/ruby/commit/… (respuesta actualizada)
Marc-André Lafortune
44
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html . Hay un módulo URI.escape en ruby ​​2.0.0. ¿Por qué fue desaprobado?
user938363
1
@ user938363 si hace clic en mostrar fuente allí, verá que todavía está marcado como obsoleto.
Drawish
229

¿Cuál es la diferencia entre un hacha y una espada y cuál debo usar? Bueno, depende de lo que necesites hacer.

URI.escapese suponía que codificaba una cadena (URL), llamada " codificación porcentual ".

CGI::escapeproviene de la especificación CGI , que describe cómo se deben codificar / decodificar los datos entre el servidor web y la aplicación.

Ahora, supongamos que necesita escapar de un URI en su aplicación. Es un caso de uso más específico. Para eso, la comunidad Ruby solía usarla URI.escapedurante años. El problema con URI.escapeera que no podía manejar la especificación 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 fue marcado como obsoleto:

Además, URI.encode actual es gsub simple. Pero creo que debería dividir un URI en componentes, luego escapar de cada componente y finalmente unirlos.

Por lo tanto, URI.encode actual se considera dañino y obsoleto. Esto será eliminado o cambiará drásticamente el comportamiento.

¿Cuál es el reemplazo en este momento?

Como dije anteriormente, el URI.encode actual es incorrecto en el nivel de especificación. Por lo tanto, no proporcionaremos el reemplazo exacto. El reemplazo variará según su caso de uso.

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

Desafortunadamente, no hay una sola palabra al respecto en los documentos, la única forma de saberlo es verificar la fuente o ejecutar el script con advertencias en el nivel detallado ( -wW2) (o usar algún google-fu).

Algunos propusieron usar CGI::Escapepara los parámetros de consulta, porque no podía escapar de un URI completo:

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::escapedebe usarse solo para parámetros de consulta, pero los resultados serán, nuevamente, contra la especificación. En realidad, el caso de uso más común es el escape de datos de formulario, como al enviar una application/x-www-form-urlencodedsolicitud POST.

También se menciona WEBrick::HTTPUtils.escapeque no hay mucha mejora (de nuevo, es solo una simple gsub, que es, IMO, incluso una opción peor 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" 

La más cercana a la especificación parece ser la gema direccionable :

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"

Tenga en cuenta que, a diferencia de todas las opciones anteriores, Addressable no se escapa #, y este es el comportamiento esperado. desea mantener el #hash en la ruta URI pero no en la consulta URI.

El único problema que queda es que no escapamos de nuestros parámetros de consulta correctamente, lo que nos lleva a la conclusión: no debemos usar un solo método para todo el URI, porque no existe una solución perfecta (hasta ahora). Como ves &, no se escapó de "Mi blog y tu blog". Necesitamos usar una forma diferente de escape para los parámetros de consulta, donde los usuarios pueden poner diferentes caracteres que tienen un significado especial en las URL. Ingrese la codificación URL. La codificación de URL debe usarse para cada valor de consulta "sospechoso", similar a lo que ERB::Util.url_encodehace:

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

Es genial, pero ya hemos requerido Direccionable:

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"

Conclusión:

  • No usar URI.escapeo similar
  • Úselo CGI::escapesi solo necesita escapar de forma
  • Si necesita trabajar con URI, use direccionable, ofrece codificación de URL, codificación de formulario y normaliza las URL.
  • Si se trata de un proyecto Rails, echa un vistazo a " ¿Cómo URL de escape de una cadena en los carriles? "
Ernesto
fuente
Muchas gracias por la información. Seguro se deshizo de algunas advertencias de prueba de azada. Un rastrillo y una azada miran abajo.
Douglas G. Allen
Gran explicación @Ernest, pero el problema con esto es que no funcionará para URL externas que no estoy tratando de crear (y no tengo control). por ejemplo, rastreadores que leen las URL de una página web y luego intenta acceder a esas URL (que deben codificarse antes del acceso).
amit_saxena
@amit_saxena si puede permitirse el lujo de tener Addressableuna de sus gemas, primero puede analizar URL, fi rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ernest
¡Interesante! Pero, de nuevo, no puedo obtener un hash de parámetros de la url original usando esto, que luego codifico como usted describe. El flujo en mi caso es: obtengo URL externas de algún feed -> que luego necesito codificar -> Pasar al cliente http para obtener contenido. Ahora, si no codifico las URL externas correctamente, los clientes HTTP basados ​​en ruby ​​fallan con errores de URI no válidos.
amit_saxena
@amit_saxena el método de análisis devolverá la instancia de Addressable:URL, luego puede llamar a todos los métodos de instancia en él, tal vez uno de ellos obtendrá los resultados deseados: rubydoc.info/gems/addressable/Addressable/URI
Ernest
6

CGI::escapees bueno para escapar del segmento de texto para que puedan usarse en los parámetros de consulta de URL (cadenas después de '?') Por ejemplo, si desea que el parámetro contenga caracteres de barra diagonal en la url, CGI :: escapa primero de esa cadena y luego insértela en la url.

Sin embargo, en Rails probablemente no lo usarás directamente. Usualmente lo usas hash.to_param, lo que usarás CGI::escapedebajo del capó.


URI::escapees bueno para escapar de una URL que no se escapó correctamente. Por ejemplo, algunos sitios web muestran una URL incorrecta / sin escape en su etiqueta de anclaje. Si su programa usa estas URL para obtener más recursos, OpenURI se quejará de que las URL no son válidas. Necesita URI::escapeestos para que sea una URL válida. Por lo tanto, se utiliza para escapar de toda la cadena URI para que sea adecuada. En mi palabra, URI :: unescape hace que una url sea legible por humanos, y URI :: escape lo hace válido para los navegadores.

Estos son los términos de mi laico y no dude en corregirlos.

lulalala
fuente
1

La diferencia es que URI.escape no funciona ...

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

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"
Radu Simionescu
fuente
2
Eligió el caso de prueba incorrecto. Los / 's,?' S y = 's son parte de un URI válido y, por lo tanto, no escapan. Otros caracteres que deben escaparse especialmente en la cadena de consulta deberían ser.
Gerard ONeill
@GerardONeill Elegí el caso de prueba precisamente para mostrar cómo URI.escape no funciona y no es confiable. ¿Está sugiriendo que URI.escape escapa solo de la cadena de consulta? ¿Cómo podría saber cuándo termina un valor de parámetro si quiero codificar un & allí? tal vez por eso es obsoleto?
Radu Simionescu
1
Eso es exactamente lo que estoy diciendo. El escape URI tiene que analizar la URL, separar lo que cree que son los parámetros individuales, escapar de ellos y volver a armarlos. Incluso eso puede ser desordenado. Pero no hace eso, solo evita escapar de algunos personajes mientras escapa del resto, lo que lo hace incompleto. Se puede usar para casos simples, especialmente si sabe que sus parámetros no serán ... confusos.
Gerard ONeill
0

CGI.escape es para escapar de un valor de URL en la cadena de consulta. Todos los personajes que no caen en ALFA, DÍGITO, '_', '-', '.' y '' el conjunto de caracteres se escapa.

Pero eso haría que una URL sea incorrecta, ya que una url debe tener '/', ':', '?', '[', '&', '=' Y ';'. Quizás más de lo que no puedo pensar fuera de mi cabeza.

URI.escape deja esos caracteres de URL solos e intenta encontrar las claves de cadena de consulta y los valores para escapar. Sin embargo, esto realmente no se puede depender ya que los valores pueden tener todo tipo de caracteres que impiden un escape fácil. Básicamente, es demasiado tarde. Pero si se puede confiar en que la URL sea simple (no '&' sy '=' s, etc. en los valores), esta función podría usarse para escapar de caracteres ilegibles o ilegibles.

En general, use siempre CGI.escape en las claves y valores individuales antes de unirlos con '&' y agregarlos después de '?'.

Gerard ONeill
fuente
0

CGI.escape no funcionó con la API OpenProject. Codificó el [] ,: y no el +. Pirateé esto juntos, lo que parece funcionar hasta ahora para la API de OpenProject. Pero estoy seguro de que le faltan algunos .gsub. Es probable que sea casi tan malo como URI.escape, pero no le dará los errores obsoletos.

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\"]")

Ambas salidas:

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

Brett
fuente