如何使用Ruby或Rails从URL中提取URL参数?

155

我有一些URL,例如:

http://www.example.com/something?param1=value1&param2=value2&param3=value3
我想从这些URL中提取参数并将其放入哈希表中。显然,我可以使用正则表达式,但我想知道是否有更容易使用Ruby或Rails实现的方法。我在Ruby模块URI中没有找到任何相关内容,但也许我错过了什么。
实际上,我需要一个可以做到这一点的方法:
extract_parameters_from_url("http://www.example.com/something?param1=value1&param2=value2&param3=value3")
#=> {:param1 => 'value1', :param2 => 'value2', :param3 => 'value3'}

你有什么建议吗?


1
Hash [*string.split('&').collect{|i|i.split('=')}.flatten] 这个也能工作,但对于这种情况来说可能是最糟糕的选择。不过你可能会发现这段代码很有趣。(我将其发布为评论,因为我不认为这是一个答案 :-)) - Vojto
10个回答

188

2
好的,那就是我错过的!当与URI.parse一起使用时,它非常完美: CGI.parse(URI.parse("http://www.example.com/something?param1=value1&param2=value2&param3=value3").query) 返回所需的哈希。感谢您的帮助。 - Flackou
98
为了更加清晰,@Flackou 希望使用以下代码:CGI.parse(URI.parse(url).query) - glenn jackman
1
我还没有测试过这个,但是第一个列出的键,包含完整的URL,似乎非常错误。 - Levi
4
我需要使用以下代码: CGI::parse(URI::parse(url).query) 该代码会解析URL中的查询参数。 - benathon
1
这个解决方案在处理数组时无法正常工作,例如 a[]=1&a[]=2 将被解析为 {"a[]"=>["1", "2"]},而Arthur的解决方案将解析为{"a"=>["1", "2"]} - Alter Lagos

179

最近我在一个项目中也需要同样的东西。在Levi的解决方案基础上,这里提供了一种更简洁、更快速的方法:

Rack::Utils.parse_nested_query 'param1=value1&param2=value2&param3=value3'
# => {"param1"=>"value1", "param2"=>"value2", "param3"=>"value3"}

比模拟Rack请求更轻量级 - Gareth
2
不错的发现。如果您有简单的参数(非嵌套)并且对性能敏感,则可能会对Rack :: Utils.parse_query感兴趣。这段代码值得一读:https://github.com/rack/rack/blob/master/lib/rack/utils.rb - Levi
谢谢,非常有用 :) - Blue Smith
1
这个方法很有效,但对于具有相同名称的复选框不起作用:param1=value1&param1=value2。第二个值会覆盖第一个值。 - B Seven
3
如果有人需要求反函数,它是 Rack::Utils.build_nested_query(params)(或者如果使用 Rack::Utils.parse_query 解析,则为 Rack::Utils.build_query(params))。请注意不要改变原意。 - zelanix
这个方法有时会因为编码问题而崩溃。我发现CGI:parse方法更加稳定(请参见上面的答案)。 - Yossi Shasho

113

刚刚在Levi的回答基础上有所改进 -

Rack::Utils.parse_query URI("http://example.com?par=hello&par2=bye").query

对于类似上面的url字符串,它会返回:

{ "par" => "hello", "par2" => "bye" } 

7
非常好的答案。简单易懂,处理完整的URL就像OP所要求的那样,结果值是字符串而不是数组,就像其他回答中那样。谢谢。 - jackocnr
4
如我之前所述,如果有人要寻找相反的操作,可以使用Rack::Utils.build_query(params)。该方法会构建查询参数字符串并返回结果。 - zelanix
2
注意 - 至少在 Ruby 1.8.7 / Rails 2.3 中并不完全是反向的。一个查询字符串 foo[]=1&foo[]=2 被正确解析为 { "foo" =>["1","2"] },但 build_query 将其转换为 "foo=1&foo=2",再次解析时会得到 { "foo"=>"2"}。 - Raels
花了我一分钟才意识到,由于缺乏包装参数,这个答案不能轻易地扩展。Rack::Utils.parse_query(URI("http://example.com?par=hello&par2=bye").query) 生成一个哈希表,可以进一步修改。 - wbharding

55

使用纯Ruby解决方案,可以将URI.parseCGI.parse结合使用(即使不需要Rails/Rack等也可以使用):

CGI.parse(URI.parse(url).query) 
# =>  {"name1" => ["value1"], "name2" => ["value1", "value2", ...] }

这很优雅。 - Jason L.

44

有多种方法可以解决你的问题。别人已经向你展示了一些技巧。我知道另一个技巧。以下是我的尝试:-

require 'uri'
url = "http://www.example.com/something?param1=value1&param2=value2&param3=value3"
uri = URI(url)
# => #<URI::HTTP:0x89e4898 URL:http://www.example.com/something?param1=value1&param2=value2&param3=value3>
URI::decode_www_form(uri.query).to_h # if you are in 2.1 or later version of Ruby
# => {"param1"=>"value1", "param2"=>"value2", "param3"=>"value3"}
Hash[URI::decode_www_form(uri.query)] # if you are below 2.1 version of Ruby
# => {"param1"=>"value1", "param2"=>"value2", "param3"=>"value3"}

阅读::decode_www_form方法的文档。


16

看看addressable宝石——它是Ruby的URI模块的流行替代品,可以轻松解析查询:

require "addressable/uri"
uri = Addressable::URI.parse("http://www.example.com/something?param1=value1&param2=value2&param3=value3")
uri.query_values['param1']
=> 'value1'

它似乎还处理参数编码/解码,与URI不同。


1
遗憾的是,就像URI库一样,这个宝石在处理URL异常时很难解析查询参数。也就是说,当URL很奇怪时,两者都无法找到查询参数,例如:http://localhost:4300/webapp/foo/#//controller/action? 这种情况。因此我不得不使用类似于 Rack::Utils.parse_nested_query(url.split("?").last) 的技巧来获取要解析的查询参数。 - Kelsey Hannan

8

使用CGI可能是一种在Ruby 2.7/3中过时的方法。

这里有一种用URI优雅地解决这个问题的方式:

uri = URI.parse 'https://duckduckgo.com/?q=ruby+programming+language'
params = Hash[URI.decode_www_form uri.query]
# => {"q"=>"ruby programming language"} 

2
不用额外的库,看这里。 - Michael Brawn

2

遗憾的是,当尝试从有缺陷的URL中提取查询参数时,URIaddressable库都会出现问题。例如,以下两个库都无法正常工作:

http://localhost:4300/webapp/foo/#//controller/action?account=001-001-111&email=john%40email.com

在Arthur / Levi的解决方案基础上,使用url.split("?").try(:last)可以抓取URL中的查询参数部分,并使用Rack::Utils.parse_nested_query将这些参数字符串解析为哈希表。

完整代码如下:

Rack::Utils.parse_nested_query(url.split("?").try(:last))

在我的示例中,返回的结果是:
{"account": "001-001-111", "email": "john@email.com"}

2

你也可以使用这种方法


require 'uri'
require 'cgi'
uri = URI("https://example.com/?query=1&q=2&query=5")
a = CGI::parse(uri.query)
puts a                   #=> {"query"=>["1", "5"], "q"=>["2"]}
puts a["query"].to_s     #=> ["1", "5"]
puts a["query"][0]       #=>  1
puts a["query"][1]       #=>  5
puts a["q"][0]           #=>  2


它更安全,也更容易。


-4
在您的控制器中,您应该能够访问一个名为params的字典(哈希)。因此,如果您知道每个查询参数的名称,那么只需执行params[:param1]来访问它... 如果您不知道参数的名称,您可以遍历该字典并获取其键。
一些简单的示例here

好的,我知道了,在控制器中使用请求的URL可以很好地工作,但如何对其他任意URL进行操作呢? - Flackou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接