RFC3986 - какие pchars нужно кодировать в процентах?
Мне нужно сгенерировать href
на URI. Все просто, за исключением случаев, когда речь идет о зарезервированных символах, которые нуждаются в процентном кодировании, например, ссылка на /some/path;element
должен появиться как <a href="/some/path%3Belement">
(Я знаю это path;element
представляет собой единое целое).
Первоначально я искал библиотеку Java, которая делает это, но в итоге я сам что-то написал (посмотрите, что не удалось с Java, так как этот вопрос не специфичен для Java).
Итак, RFC 3986 предлагает, когда НЕ нужно кодировать. Это должно произойти, как я прочитал, когда персонаж попадает под unreserved (ALPHA / DIGIT / "-" / "." / "_" / "~")
учебный класс. Все идет нормально. Но как насчет противоположного случая? RFC упоминает только этот процент (%
) всегда нужна кодировка. А что насчет остальных?
Вопрос: правильно ли считать, что все, что не является зарезервированным, может / должно быть закодировано в процентах? Например, открывающая скобка (
не обязательно кодировка, но точка с запятой ;
делает. Если я не закодирую это, я в конечном итоге ищу /first
* при следовании <a href="/first;second">
, Но следуя <a href="/first(second">
Я всегда заканчиваю тем, что искал /first(second
, как и ожидалось. Что меня смущает, так это то, что оба (
а также ;
в том же sub-delims
класс, насколько RFC идет. Как я полагаю, кодирование всего, что не является незарезервированным, - безопасная ставка, но как насчет SEOability, удобства для пользователя, когда дело доходит до локализованных URI?
Теперь, что не удалось с библиотеками Java. Я пытался сделать это какnew java.net.URI("http", "site", "/pa;th", null).toASCIISTring()
но это дает http://site/pa;th
что не хорошо. Аналогичные результаты наблюдаются с:
javax.ws.rs.core.UriBuilder
- Spring's UriUtils - я пробовал оба
encodePath(String, String)
а такжеencodePathSegment(String, String)
[*] /first
является результатом обращения к HttpServletRequest.getServletPath()
на стороне сервера при нажатии на <a href="/first;second">
РЕДАКТИРОВАТЬ: Я, вероятно, должен упомянуть, что это поведение наблюдалось при Tomcat, и я проверил, как Tomcat 6 и 7 ведут себя одинаково.
2 ответа
Правильно ли предположить, что все, что не является зарезервированным, может / должно быть закодировано в процентах?
№ RFC 3986 говорит это:
"При нормальных обстоятельствах единственное время, когда октеты в URI кодируются в процентах, это во время процесса создания URI из его составных частей. Это когда реализация определяет, какие из зарезервированных символов должны использоваться в качестве разделителей подкомпонентов, а какие может быть безопасно использован в качестве данных. "
Подразумевается, что вы решаете, какой из разделителей (то есть <delimiter>
символы) должны быть закодированы в зависимости от контекста. Те, которые не нужно кодировать, не должны кодироваться.
Например, вы не должны кодировать в процентах /
если он появляется в компоненте пути, но вы должны кодировать его в процентах, когда он появляется в запросе или фрагменте.
Так что, на самом деле, ;
характер (который является членом <reserved>
не должен автоматически кодироваться в процентах. И действительно, классы URL Java и URI этого не сделают; см. URI(...) Javadoc, в частности, шаг 7), чтобы узнать, как <path>
Компонент обрабатывается.
Это подтверждается этим пунктом:
"Цель зарезервированных символов состоит в том, чтобы предоставить набор символов-разделителей, которые можно отличить от других данных в URI. URI, которые отличаются заменой зарезервированного символа соответствующим октетом, кодированным в процентах, не эквивалентны. Кодирование процентов зарезервировано или декодирование процентного октета, который соответствует зарезервированному символу, изменит способ интерпретации URI большинством приложений. Таким образом, символы в зарезервированном наборе защищены от нормализации и, следовательно, безопасны для использования в зависимости от схемы и специфичные для производителя алгоритмы для разграничения подкомпонентов данных в URI."
Так что это говорит о том, что URL-адрес, содержащий в процентах ;
не совпадает с URL-адресом, который содержит ;
, И последнее предложение подразумевает, что они НЕ должны автоматически кодироваться или декодироваться в процентах.
Что оставляет нас с вопросом - почему вы хотите ;
быть в процентах закодированы?
Допустим, у вас есть CMS, где люди могут создавать произвольные страницы с произвольными путями. Позже мне нужно создать href ссылки на все страницы, например, в компоненте карты сайта. Поэтому мне нужен алгоритм, чтобы узнать, какие символы убежать. Точка с запятой должна рассматриваться буквально в этом случае и должна быть экранирована.
Извините, но из этого не следует, что нужно ставить точку с запятой.
Что касается спецификации URL / URI, то ;
не имеет особого значения. Это может иметь особое значение для конкретного веб-сервера / веб-сайта, но в целом (то есть без специальных знаний о сайте) у вас нет возможности узнать это.
Если
;
действительно имеет особое значение в конкретном URI, тогда, если вы его не используете в процентах, вы нарушаете это значение. Например, если сайт использует;
чтобы разрешить добавление токена сеанса к пути, тогда процентное кодирование не позволит ему распознать токен сеанса...Если
;
это просто символ данных, предоставляемый каким-либо клиентом, тогда, если вы в процентах его кодируете, вы потенциально меняете значение URI. Имеет ли это значение, зависит от того, что делает сервер; то есть, является ли декодирование или нет как часть логики приложения.
Что это значит, зная "правильные действия", требует глубоких знаний о том, что URI означает для конечного пользователя и / или сайта. Для этого потребуется продвинутая технология чтения мыслей. Моя рекомендация состоит в том, чтобы заставить CMS решить эту проблему путем надлежащего экранирования любых разделителей путей URI, прежде чем они доставят их в ваше программное обеспечение. Алгоритм обязательно будет специфичным для CMS и платформы доставки контента. Он / она будет отвечать на запросы документов, идентифицированных по URL-адресам, и ему необходимо знать, как их интерпретировать.
(Поддержка произвольных людей, использующих произвольные пути, немного сумасшедшая. Должны быть некоторые ограничения. Например, даже Windows не позволяет использовать символ разделителя файлов в компоненте имени файла. Так что у вас где-то должны быть какие-то границы. это просто вопрос решения, где они должны быть.)
ABNF для абсолютной части пути:
path-absolute = "/" [ segment-nz *( "/" segment ) ]
segment = *pchar
segment-nz = 1*pchar
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
pct-encoded = "%" HEXDIG HEXDIG
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
reserved = gen-delims / sub-delims
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
pchar
включает в себя вложенные разделы, поэтому вам не придется кодировать ни один из них в части пути: :@-._~!$&'()*+,;=
Я написал свой собственный конструктор URL, который включает в себя кодировщик для пути - как всегда, caveat emptor.