ruby 如何避免双重编码URI

atmip9wb  于 2023-05-06  发布在  Ruby
关注(0)|答案(5)|浏览(139)

如果一个URL没有被编码,它会在以后引起问题,所以我这样做了

URI.encode(url)

问题是如果URL已经被编码(无论出于什么原因),这进一步编码URL使其不可用。
除了做URI.encode(URI.decode(url)),有没有更好的方法来确保URL只被编码一次?

t9aqgxwy

t9aqgxwy1#

没有真实的的方法,您只需要跟踪字符串是否已经进行了URI转义。如果你有一个字符串,你不知道它是否已经URI转义,没有好的解决方案。
一般来说,你应该在代码中的一个点上进行编码。在内存中,所有的字符串通常不应该是URI转义的。在从URI解析组件之后,应该立即取消转义组件。在构造URI时,只在构造完整URI时对它们进行转义/编码。
如果你有一个字符串,但不知道它是否被编码,那你就不走运了;您需要跟踪,理想情况下,通过确保编码发生在清晰的系统边界上。

wfveoks0

wfveoks02#

更新:

我更新我的帖子,因为URI.encode/decode在较新版本的Ruby中被弃用。

uri = "www.example.com/%E5%86%99%E7%9C%9F/cats"

parser = URI::Parser.new
parser.escape(uri)

# => www.example.com/%25E5%2586%2599%25E7%259C%259F/cats

parser.unescape(parser.escape(uri))

# => www.example.com/%E5%86%99%E7%9C%9F/cats

我认为创建一个 Package 器URI类来处理这个问题是值得的,这样您就不必每次都这样做。您甚至可以做一些聪明的事情,比如假设URI被正确解析,如果有解析错误,则尝试将其完全转义并重新转义。

def parse(str)
  begin
    uri = self.parser.parse(str)
  rescue URI::InvalidURIError => ex
    uri = self.parser.parse(self.parser.escape(str))
  end

  new(uri)
end

虽然这确实有效,但在理想情况下,您将解析URI,并假设它们已正确编码,并且在构建URI时确保组件已正确编码。这就是为什么在构建URI对象时,我更喜欢单独转义每个组件:

def build(args)
  # these things are not used by my application, so they're unnecessary
  userinfo, port, registry, opaque = nil

  scheme   = args[:scheme]
  host     = args[:host]
  path     = encoded_path(args[:path])
  query    = args[:query]
  fragment = args[:fragment]

  uri = URI::Generic.new(scheme, userinfo, host, port, registry, path, opaque, query, fragment)

  new(uri)
end

其中encoded_path只对a-zA-Z\d_\.-~\/的路径字符集进行%编码。
当使用URI对象时,通常在字符串化之前修改querystring,所以我选择将querystring表示为哈希,当在uri对象上调用to_s时,每个值都单独用CGI.escape进行%编码。这确保了像嵌套的URI对象作为queyrstring值这样的东西被正确地转义。
您可以从IETF网站阅读有关RFC 3986的%编码和保留字符的更多信息。
尽管如此,有时仍然不可能知道URI是否% encoded

原文:

在编码之前先解码URI有什么问题吗?为了编码的安全性,你牺牲了一点性能:

uri = "www.example.com/%E5%86%99%E7%9C%9F/cats"

URI.encode(uri)

# => www.example.com/%25E5%2586%2599%25E7%259C%259F/cats

URI.encode(URI.decode(uri))

# => www.example.com/%E5%86%99%E7%9C%9F/cats

它的速度比简单编码的两倍要慢一点,但它比Addressable等替代方案快得多。

6bc51xsx

6bc51xsx3#

用户jordan对此问题有一个重复的部分有效答案
Ruby - how to encode URL without re-encoding already encoded characters)。
URI.escape在所有情况下都可以按照您想要的方式工作,除非字符已经编码。考虑到这一点,我们可以使用URI.encode的结果并使用String#gsub来“取消编码”这些字符。
下面的正则表达式查找%25(编码的%),后跟两个十六进制数字,例如%252f返回到%2f

require "uri"

DOUBLE_ESCAPED_EXPR = /%25([0-9a-f]{2})/i

def escape_uri(uri)
  URI.encode(uri).gsub(DOUBLE_ESCAPED_EXPR, '%\1')
end

puts escape_uri("https://www.example.com/url-déjà-vu")
# => https://www.example.com/url-d%C3%A9j%C3%A0-vu

这是一个有点笨拙,但它适用于所有的问题情况下,我有。更好的是,它是幂等的:

URI.encode("http://example.com/#example%example")
# => "http://example.com/%23example%25example"

URI.encode(URI.encode("http://example.com/#example%example"))
# => "http://example.com/%2523example%2525example"

escape_uri("http://example.com/#example%example")
# => "http://example.com/%23example%25example"

escape_uri(escape_uri("http://example.com/#example%example"))
# => "http://example.com/%23example%25example"
8nuwlpux

8nuwlpux4#

Addressable gem通常具有URI库中缺少的方法。在这种情况下,Addressable::URI的normalized_encode方法可以实现以下功能:

weird = "https://www.example.com/this url’s weird"
# => "https://www.example.com/this url’s weird"

encoded = Addressable::URI.normalized_encode weird
# => "https://www.example.com/this%20url%E2%80%99s%20weird"

Addressable::URI.normalized_encode encoded
# => "https://www.example.com/this%20url%E2%80%99s%20weird"

参考资料:http://www.rubydoc.info/gems/addressable/2.3.5/Addressable/URI#normalized_encode-class_method

9wbgstp7

9wbgstp75#

我不确定OpenURI中是否包含一个方法来实现这一点,因此只需与ternary运算符进行比较。

url == URI.encode(url) ? url : URI.encode(url)

它易于阅读和简单。

if_this_is_a_true_value ? then_the_result_is_this : else_it_is_this

可能还有其他方法,包括检查字符串中的某些字符等。但我认为为了保持事情的简单性和可读性,这是一个体面的解决方案。

相关问题