java.сеть.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.утиль.Ури бесполезен. Я что-то упускаю?

резюме ответов

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

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

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

4 ответов


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

a=b&c

не ошибается в URI. The RFC на универсальных состояниях синтаксиса URI

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

  query         = *uric

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

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

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

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

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

по теме:


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

единственное известное мне рабочее решение-это отражение (см. 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);

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

URLEncoder.encode("a=x%20y", "ISO-8859-1");