Что допустимо, а что нет в запросе URI? - PullRequest
85 голосов
/ 02 марта 2010

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

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

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

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

3,4. Компонент запроса

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

query = *uric

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

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

uric = reserved | unreserved | escaped

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

Это, однако, несколько разъясняется такими параграфами, как

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

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

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

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

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

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

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

Ответы [ 7 ]

62 голосов
/ 04 марта 2010

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

17 голосов
/ 07 июня 2016

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

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

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

9 голосов
/ 17 февраля 2014

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

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

6 голосов
/ 02 сентября 2011

page.html д = 1;? Д = 2; д = 3

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

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

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

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

1 голос
/ 09 декабря 2014

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

1 голос
/ 20 октября 2012

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

0 голосов
/ 09 июля 2015

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

...