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

Фон (вопрос ниже)

Я гуглял это назад и вперед, читая RFC и ТАК вопросы, пытаясь взломать это, но у меня все еще нет джека.

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

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

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

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

query = *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>

Внутри компонентов и , "/", ";", "?" защищены. Символ "/" может использоваться в 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, конечно, не является эталонной реализацией, но пока не подводит меня.

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

7 ответов

Решение

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

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

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

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

 зарезервировано = gen-delims / sub-delims

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

   sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
               / "*" / "+" / "," / ";" знак равно 

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

[...]
 pchar = незарезервировано / pct-закодировано / под-разделы / ":" / "@" 
[...]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

? Page.html д =1; д =2; д =3

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

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

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

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

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

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

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

Если вы отправляете символы ENCODED во FLASH/SWF- файл, то вы должны дважды кодировать этот символ!! (из-за парсера Flash)

Другие вопросы по тегам