Qual é a diferença entre URI.escape e CGI.escape?

147

Qual é a diferença entre URI.escapee CGI.escapee qual deles devo usar?

Tom Lehman
fonte

Respostas:

124

Havia algumas pequenas diferenças, mas o ponto importante é que URI.escapefoi preterido no Ruby 1.9.2 ... então use CGI::escapeou ERB :: Util.url_encode .

Há uma longa discussão sobre ruby-core para os interessados, que também menciona WEBrick :: HTTPUtils.escape e WEBrick :: HTTPUtils.escape_form .

Marc-André Lafortune
fonte
11
Só para aumentar a confusão - acabei de ver um comentário em stackoverflow.com/questions/4967608/…, onde alguém mencionou que o cgi escape usa '+' em vez de% 20 para espaços e que é contra a 'especificação' ...
Louis Sayers
18
uma alternativa é usar ERB::Util.url_encodeque usa adequadamente %20 para espaços
riffraff
1
@Ernest: Veja: github.com/ruby/ruby/commit/… (resposta atualizada)
Marc-André Lafortune
4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html . Existe o módulo URI.escape no ruby ​​2.0.0. Por que foi preterido?
user938363
1
@ user938363 se você clicar na fonte do programa, verá que ela ainda está marcada como obsoleta.
drewish
229

Qual é a diferença entre um machado e uma espada e qual devo usar? Bem depende do que você precisa fazer.

URI.escapedeveria codificar uma string (URL) na chamada " codificação percentual " ".

CGI::escapevem do CGI especificação , que descreve como os dados devem ser codificados / decodificados entre o servidor da Web e o aplicativo.

Agora, digamos que você precise escapar de um URI no seu aplicativo. É um caso de uso mais específico. Para isso, a comunidade Ruby usou URI.escapepor anos. O problema URI.escapeera que ele não conseguia lidar com as especificações 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 foi marcado como obsoleto:

Além disso, o URI.encode atual é simples gsub. Mas acho que ele deve dividir um URI em componentes, depois escapar de cada componente e finalmente se juntar a eles.

Portanto, o URI.encode atual é considerado prejudicial e descontinuado. Isso será removido ou mudará drasticamente o comportamento.

Qual é a substituição no momento?

Como eu disse acima, o URI.encode atual está errado no nível de especificação. Portanto, não forneceremos a substituição exata. A substituição variará de acordo com o caso de uso.

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

Infelizmente, não há uma única palavra sobre isso nos documentos, a única maneira de saber sobre isso é verificar a fonte ou executar o script com avisos no nível detalhado ( -wW2) (ou usar algum google-fu).

Alguns propuseram usar CGI::Escapepara parâmetros de consulta, porque você não conseguiu escapar de um URI inteiro:

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::escapedeve ser usado apenas para parâmetros de consulta, mas os resultados serão novamente contra as especificações. Na verdade, o caso de uso mais comum é o escape de dados do formulário, como ao enviar umapplication/x-www-form-urlencoded solicitação POST.

Também mencionado WEBrick::HTTPUtils.escapenão há muita melhoria (novamente, é apenas uma opção simples gsub, que é, na IMO, ainda pior do 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" 

O mais próximo da especificação parece ser a gema Endereçável :

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"

Observe que, ao contrário de todas as opções anteriores, o Endereçável não escapa #e esse é o comportamento esperado. você deseja manter o #hash no caminho do URI, mas não na consulta do URI.

O único problema que resta é que não escapamos adequadamente aos nossos parâmetros de consulta, o que nos leva à conclusão: não devemos usar um único método para todo o URI, porque não há solução perfeita (até agora). Como você vê, &não escapou de "Meu Blog e Seu Blog". Precisamos usar uma forma diferente de escape para parâmetros de consulta, em que os usuários podem colocar caracteres diferentes que tenham um significado especial nos URLs. Digite a codificação de URL. A codificação de URL deve ser usada para cada valor de consulta "suspeito", semelhante ao que ERB::Util.url_encodefaz:

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

É legal, mas já exigimos Endereçável:

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"

Conclusão:

  • Não use URI.escapeou similar
  • Use CGI::escapese você precisar apenas de escape de formulário
  • Se você precisar trabalhar com URIs, use Endereçável, ele oferece codificação de URL, codificação de formulário e normaliza URLs.
  • Se for um projeto do Rails, confira " Como faço para escapar URL de uma string no Rails? "
Ernest
fonte
Muito obrigado pela informação. Certamente se livrou de alguns avisos de teste de enxada. Um ancinho e uma enxada olham para baixo.
Douglas G. Allen
Ótima explicação @Ernest, mas o problema é que ele não funcionará para URLs externos que não estou tentando criar (e não tenho controle sobre). por exemplo, rastreadores que lê URLs de uma página da web e tenta acessar esses URLs (que precisam ser codificados antes do acesso).
Amit_saxena
@amit_saxena se você pode pagar ter Addressablecomo uma das suas pedras preciosas, você pode analisar URL primeiro, fi rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ernest
Interessante! Mas, novamente, não consigo obter um hash de parâmetros do URL original usando isso, que depois codifico como você descreve. O fluxo no meu caso é: eu recebo URLs externos de algum feed -> que eu preciso codificar -> passe para o cliente http para buscar conteúdo. Agora, se eu não codificar os URLs externos corretamente, os clientes HTTP baseados em ruby ​​falharão com erros de URI inválidos.
Amit_saxena
O método @amit_saxena parse retornará a instância de Addressable:URL, você pode chamar todos os métodos de instância, talvez um deles obtenha os resultados desejados: rubydoc.info/gems/addressable/Addressable/URI
Ernest
6

CGI::escapeé bom para escapar do segmento de texto para que eles possam ser usados ​​nos parâmetros de consulta de URL (sequências após '?'). Por exemplo, se você deseja ter um parâmetro contendo caracteres de barra no URL, você CGI :: escapa a string primeiro e depois a insere no URL.

No entanto, no Rails, você provavelmente não o usará diretamente. Normalmente você usa hash.to_param, o que usará CGI::escapesob o capô.


URI::escapeé bom para escapar de um URL que não foi escapado corretamente. Por exemplo, alguns sites geram URLs incorretos / sem escape em sua tag de âncora. Se o seu programa usar esses URLs para buscar mais recursos, o OpenURI reclamará que os URLs são inválidos. Você precisa URI::escapedeles para torná-lo um URL válido. Portanto, é usado para escapar de toda a cadeia de URIs para torná-la adequada. Na minha palavra, o URI :: unescape torna um URL legível por humanos, e o URI :: escape o torna válido para os navegadores.

Estes são os termos do meu leigo e fique à vontade para corrigi-los.

lulalala
fonte
1

A diferença é que o URI.escape não está funcionando ...

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

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"
Radu Simionescu
fonte
2
Você escolheu o caso de teste errado. Os / ',?' E = 'fazem parte de um URI válido e, portanto, não escapam. Outros caracteres que precisam ser escapados, especialmente na string de consulta, devem ser.
precisa saber é o seguinte
@GerardONeill Escolhi o caso de teste precisamente para mostrar como o URI.escape não está funcionando e não é confiável. Você está sugerindo que o URI.escape está escapando apenas da string de consulta? como saber quando um valor de parâmetro termina se eu quiser codificar um & lá? talvez seja por isso que é obsoleto?
Radu Simionescu 21/01/19
1
É exatamente o que estou dizendo. O escape do URI precisa analisar o URL, separar o que acha que são os parâmetros individuais, escapá-los e reuni-los novamente. Até isso pode ser confuso. Mas isso não acontece - apenas evita a fuga de alguns personagens e o resto, o que a torna incompleta. Pode ser usado para casos simples, especialmente se você souber que seus parâmetros não serão .. confusos.
precisa saber é o seguinte
0

CGI.escape é para escapar de um valor de URL na sequência de consultas. Todos os caracteres que não se enquadram no ALPHA, DIGIT, '_', '-', '.' e '' conjunto de caracteres são escapados.

Mas isso tornaria um URL incorreto, pois um URL precisa ter '/', ':', '?', '[', '&', '=' E ';'. Talvez mais do que eu possa imaginar.

O URI.escape deixa esses caracteres de URL em paz e tenta encontrar as chaves e os valores da string de consulta para escapar. No entanto, isso realmente não pode ser dependente, pois os valores podem ter todos os tipos de caracteres, impedindo uma fuga fácil. Basicamente, é tarde demais. Porém, se for possível confiar na URL como simples (sem '&' e '=' etc nos valores), essa função poderá ser usada para escapar de caracteres ilegíveis ou talvez ilegíveis.

Em geral - sempre use CGI.escape nas chaves e valores individuais antes de juntá-los a '&' e adicioná-los após o '?'.

Gerard ONeill
fonte
0

CGI.escape não funcionou com a API do OpenProject. Codificou o [] ,: e não o +. Eu hackeei isso juntos, o que parece funcionar até agora para a API do OpenProject. Mas tenho certeza de que faltam alguns .gsub. Provavelmente é quase tão ruim quanto o URI.escape, mas não fornecerá erros 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 as saídas:

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

Brett
fonte