Авторитетна позиція дублікатів HTTP GET запитних ключів


137

У мене виникають проблеми з пошуку достовірної інформації про поведінку з дублікатами рядків рядків запиту HTTP GET, наприклад

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

і зокрема, якщо замовлення зберігається чи ні. Більшість мов, орієнтованих на Інтернет, створюють масив, що містить і foo, і смужку, пов'язану з ключовим "полем", але я хотів би знати, чи існує авторитетна заява (наприклад, на RFC) про цю точку. RFC 3986 має розділ 3.4. Query, який посилається на пари key = value, але нічого не сказано про те, як інтерпретувати порядок і дублювання полів тощо. Це має сенс, оскільки це залежить від бекенда, а не в межах цього RFC ...

Хоча існує фактичний стандарт, я хотів би бачити авторитетне джерело для нього, просто з цікавості.


Вам теж цікаво про це. Інша річ - специфікація про об'єднання параметрів рядка запиту з параметрами в тілі POST.
Тило

У кодовому ранчо люди кажуть, що немає гарантії замовлення. Але що нитка старий і ніхто не підтримує це яким - або чином: coderanch.com/t/357197/Servlets/java/getParameterValues-order
Тіло

1
Окрім того, що сервер підтримує порядок рядка запиту, існує також питання про надсилання браузера в DOM (або в якомусь іншому фіксованому) порядку.
Тило

Відповіді:


112

На цьому немає ніяких специфікацій . Ви можете робити те, що вам подобається.

Типові підходи включають: перший-даний, останній-заданий, масив усіх, рядок-з'єднання-з-за-кома-всіх.

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

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'

12
Більш того, до питання, є також варіант ['рейлики', 'рубін'] (різний порядок).
Тіло

2
Можна, звичайно, зробити велику кількість речей.
yfeldblum

7
.NET дасть вам масив (я не піклувався про порядок, коли тестував це), PHP завжди дасть вам останнє, а Java (принаймні система, з якою я працював на основі Java) завжди перше значення. stackoverflow.com/questions/1809494 / ...
SimonSimCity

17
Це засновано на атаці під назвою "Забруднення параметрів HTTP" та проаналізовано OWASP : owasp.org/images/b/ba/AppsecEU09_CarettoniDiPaola_v0.8.pdf На сторінці 9 ви знайдете список 20 систем та опис того, як вони поводяться ця проблема.
SimonSimCity

1
@SimonSimCity на додаток до цього, PHP фактично створить масив, якщо до імені параметра додати квадратні дужки з необов'язковим індексом.
Мартін Ендер

14

Я можу підтвердити, що для 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.
Ян Кларк

4
@IanClark Це інтуїтивно зрозуміло для PHP-кодерів - у звичайному PHP $foo[] = 1додається до масиву. Джанго (Python) теж робить те саме.
Ізката

Може перевірити на Apache Tomcat, він повертає рядки, сполучені комами.
Gaurav

8

Відповідь yfeldblum ідеальна.

Лише зауваження про п'яту поведінку, яке я помітив нещодавно: у Windows Phone , відкриття програми з урі з повторюваним ключем запиту призведе до NavigationFailed з:

System.ArgumentException: Елемент з тим самим ключем уже додано.

Винуватець є System.Windows.Navigation.UriParsingHelper.InternalUriParseQueryStringToDictionary(Uri uri, Boolean decodeResults).

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


2
Це здається внутрішнім помилкою цієї функції, а не вибором дизайну. Можливо, функція не перевіряє наявність дублікатів ключів у створеному словнику. Словники, звичайно, вимагають унікальних ключів.
gligoran

1
Тож клієнтський браузер - а не сервер - припускає помилку в цій ситуації? Це здається помилкою. Цікаво, чи існує ця помилка і сьогодні?
Джон Шнайдер

1
@JonSchneider Так, клієнт кидає NavigationFailedтакий URI. Але, пробачте, я покинув розробку Windows (Phone) через місяць після цієї публікації і перейшов на macOS (iOS), тому більше не можу допомогти відстежувати цю проблему.
Cœur

5

Більшість фреймворків (всі?) Не надають гарантій, тому припускайте, що вони будуть повернуті у випадковому порядку.

Завжди дотримуйтесь найбільш безпечного підходу.

Наприклад, інтерфейс Java HttpServlet : ServletRequest.html # getParameterValues

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


3

Як правило, дублюються значення параметрів типу

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

приводять до одного параметра queryString, який є масивом:

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

Я бачив таку поведінку в ASP, ASP.NET і PHP4.


точно це стандарт де-факто, але, наскільки я бачу, немає авторитетного рішення щодо цього. Оскільки я не вірю, що це так, я просто не вмію його знайти.
Стефано Борині

2
Так, напевно, всі бачили таку поведінку. Питання полягало в тому, чи справді це десь вказано.
Тило

-1

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

Але я вважаю, що у Chrome і Firefox є новий клас з назвою, URLSeachParamsі він підтримує лише найпростіший формат як name=value. Якщо в рядку запиту є дублюючі імена, getметодURLSearchParams повернення лише першого.

Тож особисто, можливо, найпростіший і без дублікатів URL-адрес імен набагато безпечніший для майбутнього.


1
Якщо в рядку запиту є дублюючі імена, метод отримання URLSearchParams повертає лише перше. Це невірно: ви можете отримати все значення у вигляді масиву, використовуючиURLSearchParams.getAll('x')
Blaise,

@Blaise Дуже дякую, я раніше неправильно зрозумів функцію.
LCB
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.