Что допустимо, а что нет в запросе URI?

Предыстория (вопрос ниже)

Я гуглил это взад и вперед, читая RFC и вопросы SO, пытаясь взломать это, но у меня все еще нет разъема.

Думаю, мы просто голосуем за лучший ответ и все, или?

В основном все сводится к этому.

3.4. Компонент запроса

Компонент запроса - это строка информации, которую должен интерпретировать ресурс.

query = *uric

Внутри компонента запроса символы;, /,?,:, @, &, =, +, ,, и $ зарезервированы.

Первое, что меня смущает, это то, что * uric определяется так

uric = reserved | unreserved | escaped

reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

Однако это несколько поясняется такими параграфами, как

Вышеупомянутый зарезервированный класс синтаксиса относится к тем символам, которые разрешены в URI, но не могут быть разрешены в конкретном компоненте универсального синтаксиса URI; они используются как разделители компонентов, описанных в разделе 3.

Символы в зарезервированном наборе зарезервированы не во всех контекстах. Набор символов, фактически зарезервированных в любом данном компоненте URI, определяется этим компонентом. Как правило, символ зарезервирован, если семантика URI изменяется, если символ заменяется его экранированной кодировкой US-ASCII.

Этот последний отрывок кажется несколько отсталым, но в нем ясно сказано, что зарезервированный набор символов зависит от контекста. Тем не менее, в 3.4 говорится, что все зарезервированные символы зарезервированы в компоненте запроса, однако единственное, что может изменить семантику здесь, - это экранирование вопросительного знака (?), Поскольку URI не определяют концепцию строки запроса.

На этом этапе я полностью отказался от RFC, но нашел RFC 1738 особенно интересным.

URL-адрес HTTP принимает форму:

http://<host>:<port>/<path>?<searchpart>

В компонентах ‹path› и ‹searchpart›, /,;,? зарезервированы. Символ / может использоваться в HTTP для обозначения иерархической структуры.

Я интерпретирую это, по крайней мере, в отношении URL-адресов HTTP, которые RFC 1738 заменяет RFC 2396. Поскольку запрос URI не имеет понятия о строке запроса, также интерпретация зарезервированного не позволяет мне определять строки запроса, как я привык делаю к настоящему времени.

Вопрос

Все началось с того, что я хотел передать список чисел вместе с запросом другого ресурса. Я не придал этому особого значения и просто передал его как значения, разделенные запятыми. К моему удивлению, запятая была убрана. Кодированный запрос page.html?q=1,2,3 превратился в page.html?q=1%2C2%2C3, он работает, но некрасиво и не ожидал этого. Именно тогда я начал изучать RFC.

Мой первый вопрос: действительно ли необходимо кодировать запятые?

Мой ответ согласно RFC 2396: да, согласно RFC 1738: нет

Позже я нашел похожие сообщения о передаче списков между запросами. Где подход csv был плохим. Это появилось вместо этого (не видел этого раньше).

page.html?q=1;q=2;q=3

Мой второй вопрос, это действительный URL?

Мой ответ согласно RFC 2396: нет, согласно RFC 1738: нет (; зарезервировано)

У меня нет проблем с передачей csv, если это числа, но да, вы сталкиваетесь с риском необходимости кодировать и декодировать значения взад и вперед, если запятая вдруг понадобится для чего-то другого. В любом случае я попробовал использовать строку запроса с запятой в ASP.NET, и результат оказался не таким, как я ожидал.

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

Я не вижу, насколько это сильно отличается от подхода csv, поскольку, когда я прошу, я получаю строку с запятыми. ASP.NET, конечно, не эталонная реализация, но меня она еще не подвела.

Но самое главное - третий вопрос - а где для этого спецификация? и что бы вы сделали или не стали бы делать?


person John Leidegren    schedule 02.03.2010    source источник
comment
Как RFC 1738 может заменить RFC 2396, если RFC 2396 был опубликован почти 4 года спустя?   -  person Matthew Flaschen    schedule 02.03.2010
comment
Что касается URL-адресов и того, что практически имеет смысл, я так понимаю. (заменить, вероятно, не подходящее слово, поскольку оно использовалось в терминологии RFC для устаревших старых RFC, RFC 1738 не считает все это устаревшим, если это единственная спецификация, если найдена, которая позволяет вам поместить строку запроса в поисковую часть URL-адреса)   -  person John Leidegren    schedule 02.03.2010


Ответы (7)


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

Текущий стандарт для универсальных URI - это RFC 3986, в котором говорится следующее:

2.2. Зарезервированные символы

URI включают компоненты и подкомпоненты, разделенные символами в «зарезервированном» наборе. Эти символы называются «зарезервированными», потому что они могут (или не могут) определяться как разделители общим синтаксисом, синтаксисом каждой схемы или синтаксисом конкретной реализации алгоритма разыменования URI. Если данные для компонента URI будут конфликтовать с назначением зарезервированного символа в качестве разделителя [выделено мной], то конфликтующие данные должны быть закодированы в процентах до формирования URI.

   reserved    = gen-delims / sub-delims

   gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"

   sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
               / "*" / "+" / "," / ";" / "="

3.3. Компонент пути

[...]
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
[...]

3.4 Компонент запроса

[...]
      query       = *( pchar / "/" / "?" )

Таким образом, запятые явно разрешены в строках запроса и должны быть экранированы в данных только в том случае, если определенные схемы определяют это как разделитель. Схема HTTP не использует запятую или точку с запятой в качестве разделителя в строках запроса, поэтому их не нужно экранировать. Другой вопрос, соответствуют ли браузеры этому стандарту.

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

Что касается RFC 2396, он также позволяет использовать неэкранированные запятые в строках HTTP-запроса:

2.2. Зарезервированные символы

Многие URI включают компоненты, состоящие из определенных специальных символов или разделенные ими. Эти символы называются «зарезервированными», поскольку их использование в компоненте URI ограничено их зарезервированной целью. Если данные для компонента URI будут конфликтовать с зарезервированной целью, то конфликтующие данные должны быть экранированы перед формированием URI.

Поскольку запятые не имеют зарезервированной цели в схеме HTTP, их не нужно экранировать в данных. Замечание из п. 2.3 о зарезервированных символах, которые изменяют семантику при процентном кодировании, применяется только в общем случае; символы могут быть закодированы в процентах без изменения семантики для конкретных схем и при этом оставаться зарезервированными.

person outis    schedule 03.03.2010

Чтобы ответить, что допустимо в строке запроса, я проверил, какие специальные символы заменяются хромом при выполнении запроса:

Space -> %20
! -> !
" -> %22
# -> removed, marks the end of the query string
% -> %
& -> &
' -> %27
( -> (
) -> )
* -> *
+ -> + (this usually means blank when received at the server, so encode if necessary)
, -> ,
- -> -
. -> .
/ -> /
: -> :
; -> ;
< -> %3C
= -> =
> -> %3E
? -> ?
@ -> @
[ -> [
\ -> \
] -> ]
^ -> ^
_ -> _
` -> `
{ -> {
| -> |
} -> }
~ -> ~

Extended ASCII (like °) -> Every character from this set is encoded

Примечание. Это, вероятно, не означает, что вам не следует экранировать символы, которые не были заменены при создании URI для ссылок. Например, часто рекомендуется не использовать ~ в URI из-за проблем совместимости, но это все еще допустимый символ.

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

Итак, чтобы ответить, что следует закодировать: недопустимые символы и символы, которые вы хотите рассматривать буквально, но имеют особое значение или могут вызвать проблемы на стороне сервера.

person user764754    schedule 07.06.2016
comment
https://stackoverflow.com/questions/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b#1;c#2 - допустимый параметр запроса? - person Sumit Jain; 27.01.2020
comment
@SumitJain Нет, потому что # не может отображаться в части запроса URI как есть. Вам нужно будет закодировать его как %23, поэтому URI должен быть https://stackoverflow.com/questions/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b%231;c%232. - person Dai; 07.02.2020
comment
странный. одинарные кавычки ' разрешены в rfc3986, rfc2396 и других и оставлены encodeURIComponent, как и должно быть. Однако я также заметил, что хром избегает его в запросах URI. - person Hurelu; 10.05.2021

Просто используйте ?q=1+2+3

Я отвечаю здесь на четвертый вопрос :), который не задавал, но все началось с: как передать список чисел а-ля значения, разделенные запятыми? Мне кажется, что лучший подход - просто передать их через пробел, где пробелы будут закодированы в форме URL до +. Отлично работает, если вы знаете, что значения в списке не содержат пробелов (а числа, как правило, не содержат).

person Nas Banov    schedule 16.02.2014
comment
Хотя это должен быть комментарий (поскольку он не отвечает на вопрос), спасибо. + имеет еще больше смысла в конкретном случае, когда я хотел использовать запятую. - person Gajus; 26.07.2017

page.html? q = 1; q = 2; q = 3

это действительный URL?

да. ; зарезервирован, но не RFC. Контекст, определяющий этот компонент, - это определение типа мультимедиа application/x-www-form-urlencoded, который является частью стандарта HTML (раздел 17.13.4.1). В частности, скрытая записка, спрятанная в разделе B.2.2:

Мы рекомендуем, чтобы разработчики HTTP-сервера, и в частности разработчики CGI, поддерживали использование; вместо &, чтобы избавить авторов от необходимости экранировать символы & таким образом.

К сожалению, многие популярные серверные инфраструктуры сценариев, включая ASP.NET, не поддерживают такое использование.

person bobince    schedule 02.09.2011
comment
Таким образом, хотя запрос ?q=1;q=2;q=3 действителен, он неоднозначен: некоторые серверные фреймворки будут читать его как означающее { q: '1;q=2;q=3' }, другие могут делать это аналогично { q: {'1', '2', '3'}}. - person Nas Banov; 17.02.2014
comment
да. И что еще хуже, HTML5 теперь не включает язык о ;, а это означает, что HTML4 и HTML5 несовместимы. Ух, опасность ненормативного языка в документе спецификации ... - person bobince; 17.02.2014
comment
@NasBanov И все же другие (например, PHP) интерпретируют это как { q: 3 } - person Nicholas Shanks; 17.11.2016
comment
@NicholasShanks - там, где задействован PHP, все ставки отменены! :) - person Nas Banov; 30.07.2017

Я хотел бы отметить, что page.html?q=1&q=2&q=3 также является допустимым URL-адресом. Это вполне законный способ выражения массива в строке запроса. Ваша серверная технология определит, как именно это будет представлено.

В классическом ASP вы проверяете Response.QueryString("q").Count, а затем используете Response.QueryString("q")(0) (и (1) и (2)).

Обратите внимание, что вы тоже видели это в своем ASP.NET (я думаю, что это не было предназначено, но посмотрите):

Default.aspx?a=1;a=2&b=1&a=3

Request.QueryString["a"] = "1;a=2,3"
Request.QueryString["b"] = "1"

Обратите внимание, что точка с запятой игнорируется, поэтому вы указали a дважды и дважды получили его значение, разделенное запятой. Использование всех амперсандов Default.aspx?a=1&a=2&b=1&a=3 даст a как «1,2,3». Но я уверен, что есть способ получить каждый отдельный элемент, если сами элементы содержат запятые. Это просто свойство по умолчанию для неиндексированной QueryString, которое объединяет вложенные значения вместе с разделителями-запятыми.

person ErikE    schedule 20.10.2012

Я была такая же проблема. URL-адрес, на который была сделана гиперссылка, был сторонним URL-адресом и ожидал список параметров ТОЛЬКО в формате page.html?q=1,2,3, а URL-адрес page.html?q=1%2C2%2C3 не работал. Мне удалось заставить его работать с помощью javascript. Возможно, это не лучший подход, но вы можете проверить решение здесь, если оно кому-то поможет.

person slash    schedule 09.12.2014

Если вы отправляете закодированные символы в файл FLASH / SWF, вам следует КОДИРОВАТЬ символ дважды !! (из-за парсера Flash)

person T.Todua    schedule 09.07.2015