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

100

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

Я гуглил это взад и вперед, читая 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, поскольку, когда я прошу «a», я получаю строку с запятыми. ASP.NET, конечно, не эталонная реализация, но меня она еще не подвела.

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

Джон Лейдегрен
источник
Как RFC 1738 может заменить RFC 2396, если RFC 2396 был опубликован почти 4 года спустя?
Мэтью Флашен,
1
Что касается URL-адресов и того, что практически имеет смысл, я так понимаю. (заменить, вероятно, не подходящее слово, поскольку оно использовалось в терминологии RFC для устаревших старых RFC, RFC 1738 не считает все это устаревшим, когда это единственная спецификация, если найдена, которая позволяет вам поместить строку запроса в поисковую часть URL-адреса)
Джон Лейдегрен 02

Ответы:

69

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

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

2.2. Зарезервированные персонажи

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

   зарезервировано = общие / частичные разделители

   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 о зарезервированных символах, которые изменяют семантику при процентном кодировании, применяется только в общем случае; символы могут быть закодированы в процентах без изменения семантики для конкретных схем и при этом оставаться зарезервированными.

Outis
источник
23

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

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 из-за проблем совместимости, но это все еще допустимый символ.

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

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

user764754
источник
Является ли /programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b#1;c#2допустимый параметр запроса?
Сумит Джайн
@SumitJain Нет, потому что #не может отображаться внутри части запроса URI как есть. Вам нужно будет закодировать его как %23, чтобы URI был /programming/2366260/whats-valid-and-whats-not-in-a-uri-query?param=b%231;c%232.
Дай,
10

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

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

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

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, не поддерживают такое использование.

бобинс
источник
Таким образом, хотя ?q=1;q=2;q=3запрос действителен, он неоднозначен: одни серверные фреймворки будут читать его как означающее { q: '1;q=2;q=3' }, другие могут делать то же самое { q: {'1', '2', '3'}}.
Нас Банов
1
Да. И что еще хуже, HTML5 теперь не включает язык ;, что означает, что HTML4 и HTML5 несовместимы. Ух, опасность ненормативного языка в документе спецификации ...
bobince
@NasBanov И все же другие (например, PHP) интерпретируют это как{ q: 3 }
Николас Шанкс
1
@NicholasShanks - там, где задействован PHP, все ставки отменены! :)
Нас Банов
1

Я хотел бы отметить, что 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, которое объединяет вложенные значения вместе с разделителями-запятыми.

Эрике
источник
1

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

слэш
источник
-3

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

Т. Тодуа
источник