Перейти к основному содержимому

Руководство по кодированию/декодированию URL-адресов Java

· 4 мин. чтения

1. Обзор

Проще говоря, кодирование URL -адресов переводит специальные символы из URL-адреса в представление, которое соответствует спецификации и может быть правильно понято и интерпретировано.

В этом руководстве мы сосредоточимся на том, как кодировать/декодировать URL-адрес или формировать данные , чтобы они соответствовали спецификации и правильно передавались по сети.

2. Проанализируйте URL-адрес

Давайте сначала рассмотрим базовый синтаксис URI :

scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

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

Теперь давайте посмотрим на пример URI:

String testUrl = 
"http://www.foreach.com?key1=value+1&key2=value%40%21%242&key3=value%253";

Одним из способов анализа URI является загрузка представления String в класс java.net.URI :

@Test
public void givenURL_whenAnalyze_thenCorrect() throws Exception {
URI uri = new URI(testUrl);

assertThat(uri.getScheme(), is("http"));
assertThat(uri.getHost(), is("www.foreach.com"));
assertThat(uri.getRawQuery(),
.is("key1=value+1&key2=value%40%21%242&key3=value%253"));
}

Класс URI анализирует строковое представление URL и предоставляет его части через простой API, например, getXXX .

3. Закодируйте URL

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

Давайте закодируем данные с помощью метода encode(data, encodingScheme) класса URLEncoder :

private String encodeValue(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
}

@Test
public void givenRequestParam_whenUTF8Scheme_thenEncode() throws Exception {
Map<String, String> requestParams = new HashMap<>();
requestParams.put("key1", "value 1");
requestParams.put("key2", "value@!$2");
requestParams.put("key3", "value%3");

String encodedURL = requestParams.keySet().stream()
.map(key -> key + "=" + encodeValue(requestParams.get(key)))
.collect(joining("&", "http://www.foreach.com?", ""));

assertThat(testUrl, is(encodedURL));

Метод encode принимает два параметра:

  1. data – строка для перевода
  2. encodingScheme – имя кодировки символов

Этот метод кодирования преобразует строку в формат application/x-www-form-urlencoded .

Схема кодирования преобразует специальные символы в двухзначное шестнадцатеричное представление восьми битов, которые будут представлены в виде « %xy ». Когда мы имеем дело с параметрами пути или добавляем динамические параметры, мы будем кодировать данные, а затем отправлять их на сервер.

Примечание. В Рекомендации консорциума World Wide Web указано, что мы должны использовать кодировку UTF-8 . Невыполнение этого требования может привести к несовместимости. (Ссылка: https://docs.oracle.com/javase/7/docs/api/java/net/URLEncoder.html )

4. Расшифруйте URL-адрес

Давайте теперь декодируем предыдущий URL-адрес, используя метод декодирования URLDecoder :

private String decode(String value) {
return URLDecoder.decode(value, StandardCharsets.UTF_8.toString());
}

@Test
public void givenRequestParam_whenUTF8Scheme_thenDecodeRequestParams() {
URI uri = new URI(testUrl);

String scheme = uri.getScheme();
String host = uri.getHost();
String query = uri.getRawQuery();

String decodedQuery = Arrays.stream(query.split("&"))
.map(param -> param.split("=")[0] + "=" + decode(param.split("=")[1]))
.collect(Collectors.joining("&"));

assertEquals(
"http://www.foreach.com?key1=value 1&key2=value@!$2&key3=value%3",
scheme + "://" + host + "?" + decodedQuery);
}

Здесь нужно помнить два важных момента:

  • Анализировать URL перед декодированием
  • Используйте одну и ту же схему кодирования для кодирования и декодирования

Если бы мы декодировали, а затем анализировали, части URL-адреса могли бы быть проанализированы неправильно. Если бы мы использовали другую схему кодирования для декодирования данных, это привело бы к мусорным данным.

5. Кодировать сегмент пути

Мы не можем использовать URLEncoder для кодирования сегментов пути URL . Компонент пути относится к иерархической структуре, представляющей путь к каталогу, или служит для поиска ресурсов, разделенных «/».

Зарезервированные символы в сегментах пути отличаются от значений параметров запроса. Например, знак «+» является допустимым символом в сегментах пути и поэтому не должен кодироваться.

Вместо этого для кодирования сегмента пути мы используем класс UriUtils от Spring Framework.

`Класс UriUtils предоставляет методы encodePath и encodePathSegment` для кодирования пути и сегмента пути соответственно:

private String encodePath(String path) {
try {
path = UriUtils.encodePath(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
LOGGER.error("Error encoding parameter {}", e.getMessage(), e);
}
return path;
}
@Test
public void givenPathSegment_thenEncodeDecode()
throws UnsupportedEncodingException {
String pathSegment = "/Path 1/Path+2";
String encodedPathSegment = encodePath(pathSegment);
String decodedPathSegment = UriUtils.decode(encodedPathSegment, "UTF-8");

assertEquals("/Path%201/Path+2", encodedPathSegment);
assertEquals("/Path 1/Path+2", decodedPathSegment);
}

В приведенном выше фрагменте кода мы видим, что когда мы использовали метод encodePathSegment , он вернул закодированное значение, а + не закодировано, потому что это символ значения в компоненте пути.

Давайте добавим переменную пути к нашему тестовому URL:

String testUrl
= "/path+1?key1=value+1&key2=value%40%21%242&key3=value%253";

И чтобы собрать и утвердить правильно закодированный URL, мы изменим тест из Раздела 2:

String path = "path+1";
String encodedURL = requestParams.keySet().stream()
.map(k -> k + "=" + encodeValue(requestParams.get(k)))
.collect(joining("&", "/" + encodePath(path) + "?", ""));
assertThat(testUrl, CoreMatchers.is(encodedURL));

6. Заключение

В этой статье мы увидели, как кодировать и декодировать данные, чтобы их можно было правильно передавать и интерпретировать.

Хотя в статье основное внимание уделялось кодированию/декодированию значений параметров запроса URI, этот подход применим и к параметрам формы HTML.

Как всегда, исходный код доступен на GitHub .