Авторитетное положение повторяющихся ключей запроса HTTP GET

142

У меня возникают проблемы с поиском достоверной информации о поведении с повторяющимися полями строки запроса HTTP GET, например

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

и, в частности, соблюдается ли порядок. Большинство веб-ориентированных языков создают массив, содержащий как foo, так и bar, связанные с ключевым "field", но я хотел бы знать, существует ли авторитетное утверждение (например, в RFC) по этому поводу. В RFC 3986 есть раздел 3.4. Query, который относится к парам ключ = значение, но ничего не говорится о том, как интерпретировать порядок, дублирование полей и так далее. Это имеет смысл, поскольку это зависит от серверной части, а не в рамках этого RFC ...

Хотя стандарт де-факто существует, я бы хотел увидеть его авторитетный источник, просто из любопытства.

Стефано Борини
источник
Мне тоже было интересно об этом. Другой момент - это спецификация объединения параметров из строки запроса с параметрами в теле POST.
Тило
На кодовом ранчо люди говорят, что нет гарантии заказа. Но эта ветка устарела,
Тило
1
Помимо того, что сервер сохраняет порядок строки запроса, существует также вопрос о том, что браузер отправляет их в DOM (или другом фиксированном) порядке.
Тило

Ответы:

114

На это нет никаких спецификаций . Вы можете делать, что хотите.

Типичные подходы включают в себя: "первый заданный", "последний заданный", "массив всего", "строка-соединение-с-запятой-всего".

Предположим, необработанный запрос:

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'
Yfeldblum
источник
13
Более того, есть вариант ['rails', 'ruby'] (другой порядок).
Тило
2
Конечно, можно делать очень много вещей.
yfeldblum
7
.NET предоставит вам как массив (я не заботился о порядке, когда тестировал это), PHP всегда будет давать вам последнее значение, а Java (по крайней мере, система, с которой я работал на основе Java) всегда первое значение. stackoverflow.com/questions/1809494/…
SimonSimCity 08
17
Это основано на атаке под названием "Загрязнение параметров HTTP" и было проанализировано OWASP: owasp.org/images/b/ba/AppsecEU09_CarettoniDiPaola_v0.8.pdf На странице 9 вы найдете список из 20 систем и описание того, как они справляются с Эта проблема.
SimonSimCity
1
@SimonSimCity в дополнение к этому PHP фактически создаст массив, если вы добавите квадратные скобки с необязательным индексом к имени параметра.
Мартин Эндер
13

Могу подтвердить, что для 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.

SimonSimCity
источник
1
[]Суффикс кажется, действительно странное поведение, но если вы пытаетесь отправить массив в качестве аргумента с помощью JQuery - х .ajax(), то он будет автоматически добавлять их для вас таким же образом. Похоже, это на пользу пользователям PHP.
Ян Кларк
4
@IanClark Это интуитивно понятно для программистов PHP - на простом PHP $foo[] = 1добавляется к массиву. Django (Python) также делает то же самое.
Izkata
Можно проверить на Apache Tomcat, что он возвращает строки, объединенные запятыми.
Gaurav Ojha
8

Ответ Ифельдблюма идеален.

Просто примечание о пятом поведении, которое я заметил недавно: на Windows Phone открытие приложения с uri с повторяющимся ключом запроса приведет к NavigationFailed с:

System.ArgumentException: элемент с таким же ключом уже был добавлен.

Виновник System.Windows.Navigation.UriParsingHelper.InternalUriParseQueryStringToDictionary(Uri uri, Boolean decodeResults).

Так что система даже не позволит вам справиться с этим так, как вы хотите, она запретит это. У вас остается единственное решение - выбрать собственный формат (CSV, JSON, XML, ...) и uri-escape-it.

Cœur
источник
2
Это похоже на внутреннюю ошибку этой функции, а не на выбор дизайна. Функция вероятно не проверяет дублирующиеся ключи в создаваемом Словаре. Словари, конечно, требуют уникальных ключей.
gligoran 05
1
Значит, браузер клиента, а не сервер, выдает ошибку в этой ситуации? Это действительно похоже на ошибку. Интересно, существует ли эта ошибка сегодня?
Джон Шнайдер
1
@JonSchneider Да, клиент забрасывает NavigationFailedтакой URI. Но, простите меня, я отказался от разработки для Windows (Phone) через месяц после этой публикации и перешел на macOS (iOS), поэтому сейчас я больше не могу помогать отслеживать эту проблему.
Cœur
5

Большинство (все?) Фреймворков не дают никаких гарантий, поэтому предположим, что они будут возвращены в случайном порядке.

Всегда выбирайте самый безопасный подход.

Например, интерфейс java HttpServlet: ServletRequest.html # getParameterValues

Даже метод getParameterMap не упоминает о порядке параметров (на порядок итератора java.util.Map также нельзя полагаться).

Photodeus
источник
3

Обычно повторяющиеся значения параметров, например

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

приведет к единственному параметру queryString, который является массивом:

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

Я видел такое поведение в ASP, ASP.NET и PHP4.

3Dave
источник
Собственно, это стандарт де-факто, но, насколько я понимаю, по нему нет авторитетного решения. Поскольку я не верю, что это так, я просто не могу это найти.
Стефано Борини
2
Да, наверное, все видели такое поведение. Вопрос был в том, действительно ли это где-то указано.
Тило
-1

У меня такой же вопрос. Я пишу функцию javascript для анализа и преобразования запросов. Я не знаю, есть ли в строке запроса повторяющиеся имена или имя в скобках, например x [] = 1 & x [] = 2, является стандартным, хотя некоторые языки поддерживают этот формат.

Но я обнаружил, что у Chrome и Firefox есть новый класс с именем, URLSeachParamsи он поддерживает только самый простой формат как name=value. Если в строке запроса есть повторяющиеся имена, getметод URLSearchParamsвозвращает только первое.

Так что лично, возможно, самый простой URL-адрес без повторяющихся имен намного безопаснее в будущем.

LCB
источник
1
Если в строке запроса есть повторяющиеся имена, метод get URLSearchParams возвращает только первое из них. Это неверно: вы можете получить все значения в виде массива, используяURLSearchParams.getAll('x')
Blaise
@Blaise Большое спасибо, я неправильно понял эту функцию раньше.
LCB