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 libs. Я пробовал делать это, как, например,  new java.net.URI("http", "site", "/pa;th", null).toASCIISTring()
но это дает http://site/pa;th, что бесполезно. Аналогичные результаты наблюдаются при:

  • javax.ws.rs.core.UriBuilder
  • Spring UriUtils - я пробовал как encodePath(String, String), так и encodePathSegment(String, String)

[*] /first является результатом вызова HttpServletRequest.getServletPath() на стороне сервера при нажатии <a href="/first;second">

EDIT: Мне, вероятно, стоит упомянуть, что это поведение наблюдалось при Tomcat, и я проверил, что Tomcat 6 и 7 ведут себя одинаково.

Ответ 1

Правильно ли предположить, что все, что не является безоговорочным, может/должно быть закодировано в процентах?

Нет. RFC 3986 говорит следующее:

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

Подразумевается, что вы решаете, какой из разделителей (т.е. символов <delimiter>) необходимо закодировать в зависимости от контекста. Те, которые не требуют кодирования, не должны кодироваться.

Например, вы не должны процитировать / в процентах, если он появляется в компоненте пути, но вы должны преобразовать его в процентах, когда он появляется в запросе или фрагменте.

Итак, на самом деле символ ; (который является членом <reserved>) не должен автоматически кодироваться процентом. И действительно, классы java URL и URI не будут этого делать, см. URI (...) javadoc, в частности шаг 7), как обрабатывается компонент <path>.

Это подкрепляется этим параграфом:

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

Таким образом, это говорит о том, что URL-адрес, содержащий процентный код ;, не совпадает с URL-адресом, который содержит raw ;. И последнее предложение подразумевает, что они НЕ должны быть закодированы или декодированы автоматически.


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

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

Извините, но не следует, что точка с запятой должна быть экранирована.

Что касается спецификации URL/URI, то ; не имеет особого значения. Это может иметь особый смысл для конкретного веб-сервера/веб-сайта, но в целом (т.е. Без специального знания сайта) вы не можете этого знать.

  • Если ; имеет особый смысл в определенном URI, то, если вы процентов-убегаете, то вы нарушаете это значение. Например, если сайт использует ;, чтобы добавить токен сеанса к пути, тогда процентное кодирование остановит его от распознавания токена сеанса...

  • Если ; - это просто символ данных, предоставляемый некоторым клиентом, то, если вы его кодируете, вы потенциально меняете значение URI. Неважно, зависит ли это от того, что делает сервер; то есть, является ли декодирование или нет частью логики приложения.

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

(Поддержка произвольных людей, использующих произвольные пути, немного сумасшедшая. Должны быть некоторые ограничения. Например, даже Windows не позволяет использовать символ разделителя файлов в компоненте имени файла. Таким образом, вам придется иметь некоторые границы где-то. Это просто вопрос, где они должны быть.)

Ответ 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    = "!" / "$" / "&" / "'" / "(" / ")"
               / "*" / "+" / "," / ";" / "="

pchar включает субграницы, поэтому вам не нужно было бы кодировать их в части пути: :@-._~!$&'()*+,;=

Я написал свой собственный построитель URL, который включает в себя кодер для пути - как всегда, caveat emptor.