Java.net.URI и проценты в значении параметра запроса

System.out.println(
    new URI("http", "example.com", "/servlet", "a=x%20y", null));

Результат http://example.com/servlet?a=x%2520y, где значение параметра запроса отличается от заданного. Странно, но это происходит после Javadoc:

"Процентный символ (" % ") всегда цитируется этими конструкторами".

Мы можем передать декодированную строку a=x y, а затем получим разумный результат (?) a=x%20y.

Но что, если значение параметра запроса содержит "&" персонаж? Это происходит, например, если значение представляет собой URL-адрес с параметрами запроса. Посмотрите на эту (неправильную) строку запроса: a=b&c. Амперсанд должен быть экранирован здесь (a=b%26c), в противном случае это можно рассматривать как параметр запроса a=b и некоторый мусор (c). Если я передам это конструктору URI, он закодирует его и вернет неверный URL-адрес: ...?a=b%2526c

Эта проблема, похоже, делает java.util.URI бесполезным. Я что-то пропустил?

Сводка ответов

java.net.URI знает о существовании части запроса URI, но не понимает внутренности части запроса, которая может различаться для каждой схемы. Например, java.net.URI не понимает внутреннюю структуру части HTTP-запроса. Это не будет проблемой, если java.net.URI рассмотрел запрос как непрозрачную строку и не изменил его. Но он пытается применить какой-то общий алгоритм кодирования процентов, который разбивает URL-адреса HTTP.

Поэтому я не могу использовать класс URI для надежной сборки URL-адреса из его частей, несмотря на то, что для него есть конструкторы. Я хотел бы также упомянуть, что с Java 7 реализация операции релятивизации весьма ограничена, работает только в том случае, если один URL является префиксом другого. Эти две функции (и более компактный интерфейс для этих целей) были причиной того, что меня интересовал java.net.URI, но ни один из них не работает для меня.

В конце я использовал java.net.URL для синтаксического анализа и написал код для сборки URL-адреса из частей и для релятивизации двух URL-адресов. Я также проверил класс Apache HttpClient URIBuilder и хотя он понимает внутренности строки запроса HTTP, но по состоянию на 4.3, он имеет ту же проблему с кодировкой, что и java.net.URI, когда имеет дело с частью запроса в целом.

+6
источник поделиться
4 ответа

Строка запроса

a=b&c

не является ошибочным в URI. RFC в общих синтаксисах URI

Компонент запроса представляет собой строку информации для интерпретации ресурс.

  query         = *uric

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

Символ & в строке запроса очень важен (uric представляет зарезервированные, знаковые и буквенно-цифровые символы). RFC также заявляет

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

Поскольку & является допустимым, но зарезервированным, пользователь должен определить, предназначено ли оно для кодирования или нет.

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

по теме:

+1
источник

Единственным обходным решением, которое я нашел, было использование конструкторов и методов с одним аргументом. Обратите внимание, что вы должны использовать URI#getRawQuery(), чтобы избежать декодирования %26. Например:

URI uri = new URI("http://a/?b=c%26d&e");
// uri.getRawQuery() equals "b=c%26d&e"

uri = new URI(new URI(uri.getScheme(), uri.getAuthority(),
        uri.getPath(), null, null) + "?f=g%26h&i");
// uri.getRawQuery() equals "f=g%26h&i"

uri = uri.resolve("?j=k%26l&m");
// uri.getRawQuery() equals "j=k%26l&m"
// uri.toString() equals "http://a/?j=k%26l&m"
+1
источник

Единственное рабочее решение, известное для меня, - отражение (см. https://blog.stackhunter.com/2014/03/31/encode-special-characters-java-net-uri/)

URI uri = new URI("http", null, "example.com", -1, "/accounts", null, null);
Field field = URI.class.getDeclaredField("query");
field.setAccessible(true);
field.set(uri, encodedQueryString);
//clear cached string representation
field = URI.class.getDeclaredField("string");
field.setAccessible(true);
field.set(uri, null);
0
источник

Используйте метод URLEncoder.encode(), например, в вашем случае:

URLEncoder.encode("a=x%20y", "ISO-8859-1");
-1
источник

Посмотрите другие вопросы по меткам или Задайте вопрос