URI.escape和CGI.escape有什么区别?


147

URI.escapeCGI.escape和应该使用哪一个有什么区别?

Answers:


124

两者之间有一些细微的差别,但重要的一点是Ruby 1.9.2中URI.escape弃用 ...,因此请使用CGI::escapeERB :: Util.url_encode

在ruby-core上有很多有兴趣的人进行长期讨论其中还提到了WEBrick :: HTTPUtils.escapeWEBrick :: HTTPUtils.escape_form


11
只是增加了混乱-我刚刚在stackoverflow.com/questions/4967608/…上看到一条评论,其中有人提到cgi转义使用'+'代替%20来表示空格,并且违反了'spec'...
Louis Sayers 2012年

18
一种替代方法是ERB::Util.url_encode正确使用%20 空间
riffraff 2012年

1
@Ernest:请参阅:github.com/ruby/ruby/commit/...(回答更新)
马克-安德烈·Lafortune

4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html。在ruby 2.0.0中有URI.escape模块。为什么不推荐使用?
user938363 2013年

1
@ user938363,如果您单击那里的显示源,您将看到它仍被标记为已弃用。
drewish

229

斧头和剑有什么区别,我应该使用哪把?好吧,这取决于您需要做什么。

URI.escape应该将字符串(URL)编码为所谓的“ 百分比编码 ”。

CGI::escape来自CGI规范,该规范描述了如何在Web服务器和应用程序之间对数据进行编码/解码。

现在,假设您需要在应用程序中转义URI。这是一个更具体的用例。为此,Ruby社区使用URI.escape了多年。问题URI.escape在于它无法处理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 被标记为过时的:

此外,当前的URI.encode是简单的gsub。但是我认为它应该将URI拆分为组件,然后转义每个组件,最后加入它们。

因此,当前的URI.encode被视为有害和不推荐使用。这将被删除或彻底改变行为。

这时的替代品是什么?

正如我上面所说,当前的URI.encode在规范级别上是错误的。因此,我们将不提供确切的替代品。替换将因其用例而异。

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

不幸的是,文档中对此一言不发,唯一了解它的方法是检查源代码,或在详细级别(-wW2)中运行带有警告的脚本(或使用一些google-fu)。

有人建议将其CGI::Escape用于查询参数,因为您无法转义整个URI:

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::escape应该仅用于查询参数,但结果将再次违反规范。实际上,最常见的用例是转义表单数据,例如在发送application/x-www-form-urlencodedPOST请求时。

还提到了WEBrick::HTTPUtils.escape很多改进(同样,这只是一个简单的gsub,即IMO,甚至比糟糕的选择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" 

与规格最接近的似乎是可寻址宝石:

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"

注意,与以前的所有选项不同,Addressable不会转义#,这是预期的行为。您想将#散列保留在URI路径中,而不要保留在URI查询中。

剩下的唯一问题是我们没有适当地转义查询参数,这使我们得出结论:我们不应该对整个URI使用单个方法,因为到目前为止还没有完美的解决方案。如您所见&,“我的博客和您的博客”并未逃脱。我们需要对查询参数使用不同的转义形式,用户可以在URL中放置具有特殊含义的不同字符。输入网址编码。URL编码应用于每个“可疑”查询值,类似于ERB::Util.url_encode

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

很酷,但是我们已经需要Addressable:

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"

结论:

  • 请勿使用URI.escape或类似
  • 使用CGI::escape,如果你只需要形成逃生
  • 如果您需要使用URI,请使用Addressable,它提供URL编码,表单编码并标准化URL。
  • 如果这是一个Rails项目,请查看“ 如何在Rails中对URL进行转义?

非常感谢您提供的信息。它肯定摆脱了一些测试的警告。耙子和a头从下面看。
Douglas G. Allen

@Ernest很好的解释,但是问题在于它不适用于我不想创建的外部URL(并且无法控制)。例如,搜寻器从网页读取URL,然后尝试访问那些URL(在访问之前需要对其进行编码)。
amit_saxena 2014年

@amit_saxena,如果您负担得起Addressable作为自己的宝石之一,则可以先解析URL,网址为rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ernest

有趣!但是同样,我无法使用此方法从原始网址中获取参数的哈希值,然后按照您的描述进行编码。在我的情况下,流程是:我从某些提要中获取外部URL->然后需要对其进行编码->传递给http客户端以获取内容。现在,如果我没有正确地编码外部URL,则基于ruby的HTTP客户端会因无效的URI错误而失败。
amit_saxena 2014年

@amit_saxena解析方法将返回的实例方法Addressable:URL,然后可以在其上调用所有实例方法,也许其中之一将为您带来所需的结果:rubydoc.info/gems/addressable/Addressable/URI
Ernest


6

CGI::escape是用于转义文本段的好方法,因此可以在url查询参数(“?”之后的字符串)中使用它们。例如,如果要在URL中包含包含斜杠字符的参数,则应先CGI :: escape该字符串,然后将其插入URL。

但是在Rails中,您可能不会直接使用它。通常您使用hash.to_param,它将CGI::escape在引擎盖下使用。


URI::escape有助于转义未正确转义的网址。例如,某些网站在其锚标记中输出错误/未转义的网址。如果您的程序使用这些URL来获取更多资源,则OpenURI将抱怨这些URL无效。您需要URI::escape这些使其成为有效的URL。因此,它用于转义整个URI字符串以使其正确。用我的话说,URI :: unescape使URL可以被人类读取,而URI :: escape使它对浏览器有效。

这些是我的外行术语,可以随时更正。


1

区别在于URI.escape无法正常工作...

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

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"

2
您选择了错误的测试用例。/,?和=都是有效URI的一部分,因此无法转义。其他需要转义的字符,特别是在查询字符串中也应转义。
Gerard ONeill

@GerardONeill我精确地选择了测试用例,以显示URI.escape如何不起作用且不可靠。您是否建议URI.escape仅转义查询字符串?如果要在其中编码&,怎么知道参数值何时结束?也许那就是为什么它过时了?
Radu Simionescu

1
正是我的意思。URI转义必须解析URL,分离出它认为是各个参数的内容,将其转义并将其放回原处。即使那样也可能是混乱的。但这并没有这样做-只是避免了转义某些字符而转义其余字符,这使其不完整。它可以用于简单的情况,尤其是当您知道参数不会引起混淆时。
Gerard ONeill

0

CGI.escape用于转义查询字符串中的URL值。所有不属于ALPHA,DIGIT,'_','-','。'的字符 和''字符集被转义。

但这会使URL不正确,因为URL需要具有'/','::,'?','[','&','='和';'。也许还有更多我想不到的东西。

URI.escape仅保留这些URL字符,然后尝试查找要转义的查询字符串键和值。但是,实际上不能依赖于此,因为值可以具有各种字符,从而容易逃脱。基本上,为时已晚。但是,如果可以将URL简化为简单的值(值中没有'&'和'='等),则可以使用此函数来转义可能不可读或非法的字符。

通常,在将各个键和值与'&'联接并在'?'之后添加它们之前,请始终对各个键和值使用CGI.escape。


0

CGI.escape不适用于OpenProject API。它对[] ,:而不是+进行编码。我一起破解了这一点,到目前为止看来,它对于OpenProject的API仍然有效。但我确定它缺少一些.gsub。它可能几乎与URI.escape一样糟糕,但不会给您过时的错误。

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

两种输出:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.