Выборочное кодирование URL в Java

На вход приходит строка вида

"http://127.0.0.1/servername/example.html?param1=value1&param2=value2&param3=Иванов Иван Иванович".

Сплитом отделяю параметры (по знаку вопроса), пропускаю через java.net.URLEncoder.encode. Соединяю обратно, получаю:

"http://127.0.0.1/servername/example.html?param1%3Dvalue1%26param2%3Dvalue2%26param3%3D%D0%98%D0%B2%D0%B0%D0%BD%D0%BE%D0%B2%20%D0%98%D0%B2%D0%B0%D0%BD%20%D0%98%D0%B2%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%87"

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

Если я первую ссылку запущу в хроме, то он её экранирует нормально: "http://127.0.0.1/servername/example.html?param1=value1&param2=value2&param3=%D0%98%D0%B2%D0%B0%D0%BD%D0%BE%D0%B2%20%D0%98%D0%B2%D0%B0%D0%BD%20%D0%98%D0%B2%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%87"

Вопрос: почему браузер умеет, а Java нет? Как в джаве сделать такое же экранирование, не опускаясь до ручного хардкода замены каждой отдельной кириллической буквы?


Ответы (1 шт):

Автор решения: Aarne Avialaynen

В поисках набрёл на эту тему и слегка допилил один из вариантов оттуда. Работает.

private static final String HEX = "0123456789ABCDEF";

public static String encodeURIComponent(String str) {
    if (str == null) {
        return null;
    }

    byte[] bytes = str.getBytes(Charset.forName("UTF8"));
    StringBuilder builder = new StringBuilder(bytes.length);

    for (byte c : bytes) {
        if ((c >= 'a' && c <= 'z')
                || (c >= 'A' && c <= 'Z')
                || (c >= '0' && c <= '9')
                || c == '~' || c == '_' || c == '-' || c == '.' 
                || c == '=' || c == '&' || c == '%') {
            builder.append((char) c);
        } else {
            builder.append('%')
                    .append(HEX.charAt(c >> 4 & 0xf))
                    .append(HEX.charAt(c & 0xf));
        }
    }

    return builder.toString();
}
→ Ссылка