#java #language-agnostic #rfc #rfc3986
#java #не зависит от языка #rfc #rfc3986
Вопрос:
Мне нужно сгенерировать href
в URI. Все просто, за исключением зарезервированных символов, которым требуется кодировка в процентах, например, ссылка на /some/path;element
должна отображаться как <a href="/some/path;element">
(я знаю, что 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. Как я себе представляю, кодирование всего, что не является неограниченным, — это безопасная ставка, но как насчет доступности поиска, удобства для пользователя, когда дело доходит до локализованных URI?
Теперь, что не удалось с библиотеками Java. Я пытался сделать это следующим образом
new java.net.URI("http", "site", "/pa;th", null).toASCIISTring()
но это дает http://site/pa;th
то, что никуда не годится. Аналогичные результаты, наблюдаемые с:
javax.ws.rs.core.UriBuilder
- UriUtils от Spring — я пробовал оба
encodePath(String, String)
иencodePathSegment(String, String)
[*] /first
является результатом вызова HttpServletRequest.getServletPath()
на стороне сервера при нажатии на <a href="/first;second">
РЕДАКТИРОВАТЬ: вероятно, мне нужно упомянуть, что такое поведение наблюдалось в Tomcat, и я проверил, что Tomcat 6 и 7 ведут себя одинаково.
Ответ №1:
Правильно ли предполагать, что все, что не является необслуживаемым, может / должно быть закодировано в процентах?
В RFC 3986 не указано это:
«При нормальных обстоятельствах единственный раз, когда октеты в URI кодируются в процентах, — это в процессе создания URI из его составных частей. Это когда реализация определяет, какие из зарезервированных символов следует использовать в качестве разделителей подкомпонентов, а какие можно безопасно использовать в качестве данных. «
Подразумевается, что вы решаете, какие из разделителей (т. Е. <delimiter>
символов) должны быть закодированы в зависимости от контекста. Те, которые не нужно кодировать, не должны кодироваться.
Например, вы не должны кодировать a в процентах /
, если он появляется в компоненте path, но вы должны кодировать его в процентах, когда он появляется в запросе или фрагменте.
Таким образом, на самом деле, ;
символ (который является членом <reserved>
) не должен автоматически кодироваться в процентах. И действительно, классы java URL и URI не будут этого делать; смотрите URI (…) javadoc, в частности, шаг 7) о том, как обрабатывается <path>
компонент.
Это подтверждается этим абзацем:
«Назначение зарезервированных символов — предоставить набор символов-разделителей, которые можно отличить от других данных в URI. URI, которые отличаются заменой зарезервированного символа соответствующим октетом с кодировкой в процентах, не эквивалентны. Процентное кодирование зарезервированного символа или декодирование октета с кодировкой в процентах, соответствующего зарезервированному символу, изменит способ интерпретации URI большинством приложений. Таким образом, символы в зарезервированном наборе защищены от нормализации и, следовательно, безопасны для использования специфичными для схемы и производителя алгоритмами для разграничения подкомпонентов данных в URI.»
Итак, это говорит о том, что URL, содержащий кодировку в процентах, ;
не совпадает с URL, который содержит raw ;
. И последнее предложение подразумевает, что они не должны быть закодированы в процентах или декодированы автоматически.
Что оставляет нас с вопросом — почему вы хотите ;
, чтобы они были закодированы в процентах?
Допустим, у вас есть CMS, где пользователи могут создавать произвольные страницы с произвольными путями. Позже мне нужно сгенерировать ссылки href на все страницы, например, в компоненте карты сайта. Поэтому мне нужен алгоритм, чтобы знать, какие символы следует экранировать. Точка с запятой в этом случае должна обрабатываться буквально и должна быть экранирована.
Извините, но из этого не следует, что точка с запятой должна быть экранирована.
Что касается спецификации URL / URI, ;
не имеет особого значения. Это может иметь особое значение для конкретного веб-сервера / веб-сайта, но в целом (т. Е. без специальных знаний о сайте) у вас нет способа узнать это.
-
Если
;
действительно имеет особое значение в определенном URI, то если вы экранируете его в процентах, то вы нарушаете это значение. Например, если сайт использует;
, чтобы разрешить добавление токена сеанса к пути, то кодирование в процентах не позволит ему распознать токен сеанса … -
Если
;
это просто символ данных, предоставленный каким-либо клиентом, то, кодируя его в процентах, вы потенциально изменяете значение URI. Имеет ли это значение, зависит от того, что делает сервер; т. Е. декодируется ли is или нет как часть логики приложения.
Что это значит, знание того, что «правильно делать», требует глубокого знания того, что URI означает для конечного пользователя и / или сайта. Для реализации этого потребуется передовая технология чтения мыслей. Моя рекомендация заключалась бы в том, чтобы заставить CMS решить эту проблему, соответствующим образом экранируя любые разделители путей URI, прежде чем она доставит их в ваше программное обеспечение. Алгоритм обязательно будет специфичным для CMS и платформы доставки контента. ИТ / они будут отвечать на запросы о документах, идентифицируемых URL-адресами, и им нужно будет знать, как их интерпретировать.
(Поддержка произвольных пользователей, использующих произвольные пути, немного сумасшедшая. Должны быть некоторые ограничения. Например, даже Windows не позволяет использовать символ-разделитель файлов в компоненте filename. Итак, вам где-то понадобятся какие-то границы. Это просто вопрос решения, где они должны быть.)
Комментарии:
1. Допустим, у вас есть CMS, где пользователи могут создавать произвольные страницы с произвольными путями. Позже мне нужно сгенерировать ссылки href на все страницы, например, в компоненте карты сайта. Поэтому мне нужен алгоритм, чтобы знать, какие символы следует экранировать. Точка с запятой в этом случае должна обрабатываться буквально и должна быть экранирована.
Ответ №2:
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 = "!" / "$" / "amp;" / "'" / "(" / ")"
/ "*" / " " / "," / ";" / "="
pchar
включает вложенные разделители, поэтому вам не придется кодировать какие-либо из них в части path: :@-._~!$amp;'()* ,;=
Я написал свой собственный конструктор URL, который включает кодировщик для пути — как всегда, будьте внимательны.
Комментарии:
1. Мхм, ты хочешь сказать, что
;
это не требует кодирования в процентах? Я тоже так думал вначале, но это кажется неправильным (см. Последнюю главу моего поста). Или это может быть ошибка Tomcat?2. @mindas — Я бы предположил, что Tomcat следует более старой спецификации URI — RFC 2396 . В этой спецификации
;
и=
зарезервированы для параметров в сегментах пути. Некоторые серверы приложений все еще реализуют их для отслеживания сеанса с помощью перезаписи URL (/foo/bar;jsessionid=***
). Если вы решите загрузить мою библиотеку, смотритеEditPathParams.java
в примерах.