重复的HTTP GET查询键的权威位置

157

我在查找关于 HTTP GET 查询字符串重复字段的行为的权威信息时遇到了困难,例如:

http://example.com/page?field=foo&field=bar 

特别是如果保留顺序或不保留顺序。大多数面向Web的语言生成一个包含与键“field”相关联的foo和bar的数组,但我想知道是否存在权威声明(例如在RFC上)关于这一点。RFC 3986有一个名为3.4. Query的章节,涉及键=值对,但没有提到如何解释订单、重复字段等等。这是有意义的,因为它取决于后端,并且不在该RFC的范围内...

尽管存在事实上的标准,但出于好奇,我想看到权威来源。


我也一直在想这个问题。另一个问题是关于将查询字符串中的参数与POST请求体中的参数合并的规范。 - Thilo
在代码牧场上,人们说没有顺序保证。但是那个帖子很旧了,没有人以任何方式支持它:http://www.coderanch.com/t/357197/Servlets/java/getParameterValues-order - Thilo
1
除了服务器保持查询字符串的顺序外,还存在浏览器按DOM(或其他固定)顺序发送它们的问题。 - Thilo
7个回答

130

关于这个问题没有具体规范,你可以按照自己的喜好进行处理。

典型的方法包括:first-given、last-given、array-of-all、string-join-with-comma-of-all。

假设原始请求是:

GET /blog/posts?tag=ruby&tag=rails HTTP/1.1
Host: example.com

然后根据语言或框架,request.query['tag']应产生不同的选项:

request.query['tag'] => 'ruby'
request.query['tag'] => 'rails'
request.query['tag'] => ['ruby', 'rails']
request.query['tag'] => 'ruby,rails'

14
更进一步回答问题,还有一个选项是['rails', 'ruby'](顺序不同)。 - Thilo
2
一个人肯定可以做很多事情。 - yfeldblum
8
.NET会将未选中的复选框作为数组返回(在我的测试中,我没有关心顺序),PHP始终会返回最后一个未选中的复选框,而Java(至少我使用的基于Java的系统)总是返回第一个未选中的复选框。 - SimonSimCity
18
这基于一种名为HTTP参数污染的攻击方式,已经被OWASP分析过:https://www.owasp.org/images/b/ba/AppsecEU09_CarettoniDiPaola_v0.8.pdf 在第9页,您将找到一个包含20个系统及其如何处理此问题的描述列表。 - SimonSimCity
1
@SimonSimCity 此外,如果您在参数名称后附加方括号和可选索引,PHP实际上会创建一个数组。 - Martin Ender
显示剩余2条评论

18
自从12年前提出这个问题并得到接受的答案以来,情况似乎已经发生了变化。我相信现在我们有了权威的来源:WHATWG URL标准在第6.2节(https://url.spec.whatwg.org/#interface-urlsearchparams)和第5.1节(https://url.spec.whatwg.org/#urlencoded-parsing)中详细描述了提取和解析查询字符串的过程。解析输出是“最初为空的名称-值元组列表,其中名称和值都是字符串”,其中列表被定义为有限的有序序列,并且键值对按它们在URL中出现的顺序添加到此列表中。首先没有提到重复的键,但是在第6.2节(https://url.spec.whatwg.org/#interface-urlsearchparams)中,URLSearchParams类上的一些方法设置了明确的排序期望:“getAll(name)方法的步骤是返回名称为name的所有名称-值对的值... 按照列表顺序”;sort()方法指定“具有相同名称的名称-值对之间的相对顺序必须保持不变。”(强调我的)。检查提交中引用的Github问题时,我们可以看到最初的建议是在键相同的情况下按值进行排序,但是后来进行了更改:“默认排序不影响值的顺序的原因是值的顺序可能很重要。我们不应该假设移动值的顺序是可以的。”(https://github.com/whatwg/url/issues/26#issuecomment-271600764

15

我可以确认对于PHP(至少在版本4.4.4及以上),它的工作方式如下:

GET /blog/posts?tag=ruby&tag=rails HTTP/1.1
Host: example.com

导致结果为:

request.query['tag'] => 'rails'

但是

GET /blog/posts?tag[]=ruby&tag[]=rails HTTP/1.1
Host: example.com

结果是:

request.query['tag'] => ['ruby', 'rails']

无论是GET还是POST数据,此行为都是相同的。


1
[] 后缀看起来是非常奇怪的行为,但如果你尝试通过 jQuery 的 .ajax() 方法将一个数组作为参数发送,它会自动为你添加这些后缀。看起来这对 PHP 用户是有益的。 - Ian Clark
4
对于熟悉 PHP 的开发者来说很容易理解——在纯 PHP 中,$foo[] = 1 表示将 1 添加到数组中。Django(Python)也是同样的处理方式。 - Izkata
可以在Apache Tomcat上验证它返回逗号连接的字符串。 - Gaurav Ojha

8

yfeldblum的回答非常完美。

只是关于最近我注意到的第五种行为的一个注释:在Windows Phone上,使用重复查询键的uri打开应用程序将导致NavigationFailed,并出现以下错误:

System.ArgumentException: An item with the same key had already been added.

罪魁祸首是System.Windows.Navigation.UriParsingHelper.InternalUriParseQueryStringToDictionary(Uri uri, Boolean decodeResults)

因此,系统甚至不会让您按照自己的方式处理它,而是禁止了它。你只能选择你自己的格式(CSV,JSON,XML等)并进行uri-escape。


2
那似乎是该函数的内部错误,而不是设计选择。可能的函数在创建字典时没有检查重复键。当然,字典需要唯一的键。 - gligoran
1
所以在这种情况下,是客户端浏览器而不是服务器抛出了错误?这似乎是一个bug。我想知道这个bug今天是否仍然存在? - Jon Schneider
1
@JonSchneider 是的,客户端对这样的URI抛出了“NavigationFailed”异常。但是,请原谅我,在发布后一个月,我放弃了Windows(Phone)开发并转向了macOS(iOS),所以现在无法再帮助跟踪此问题了。 - Cœur

5
大多数(全部?)框架都不提供保证,因此请假设它们将以随机顺序返回。始终采取最安全的方法。例如,Java HttpServlet接口:ServletRequest.html#getParameterValues。即使是 getParameterMap 方法也没有提及参数顺序(java.util.Map迭代器的顺序也不能依赖)。

提供没有保证,或者可能严重缺乏文档,并夹杂着一些牛仔编程? - Martin Andersson

3
通常,重复的参数值例如:
http://example.com/page?field=foo&field=bar

将结果作为单个queryString参数返回,该参数是一个数组:

field[0]=='foo'
field[1]=='bar'

我曾在ASP、ASP.NET和PHP4中见过这种行为。

准确地说,这是事实上的标准,但就我所看到的情况来看,还没有权威的决定。由于我不相信是这种情况,我只是找不到它。 - Stefano Borini
2
是的,可能每个人都见过那种行为。问题是它是否实际上有规定。 - Thilo

1

?array[]=value1&array[]=value2 方法肯定是非常流行的一种。

  • 被大多数 JavaScript 框架支持
  • 被 Java Spring 支持
  • 被 PHP 支持

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