1. введение
Проще говоря, кодировка URL переводит специальные символы из URL-адреса в представление, которое соответствует спецификации и может быть правильно понято и интерпретировано.
В этой статье мы сосредоточимся на том, как кодировать/декодировать URL-адрес или данные формы , чтобы они соответствовали спецификации и правильно передавались по сети.
2. Проанализируйте URL-адрес
Базовый синтаксис URI можно обобщить следующим образом:
scheme:[//[user:[email protected]]host[:port]][/]path[?query][#fragment]
Первым шагом в кодировании URI является изучение его частей, а затем кодирование только соответствующих частей.
Давайте рассмотрим пример URI:
String testUrl = "http://www.baeldung.com?key1=value+1&key2=value%40%21%242&key3=value%253";
Одним из способов анализа URI является загрузка строкового представления в 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.baeldung.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, encoding Scheme) класса URLEncoder :
private String encodeValue(String value) { return URLEncoder.encode(value, StandardCharsets.UTF_8.toString()); } @Test public void givenRequestParam_whenUTF8Scheme_thenEncode() throws Exception { MaprequestParams = new HashMap<>(); requestParams.put("key1", "value 1"); requestParams.put("key2", "[email protected]!$2"); requestParams.put("key3", "value%3"); String encodedURL = requestParams.keySet().stream() .map(key -> key + "=" + encodeValue(requestParams.get(key))) .collect(joining("&", "http://www.baeldung.com?", "")); assertThat(testUrl, is(encodedURL));
Метод encode принимает два параметра:
- data – строка для перевода
- encodingScheme – имя кодировки символов
Этот метод encode преобразует строку в формат application/x-www-form-urlencoded|/.
Схема кодирования преобразует специальные символы в двухзначное шестнадцатеричное представление из 8 бит, которое будет представлено в виде ” %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.baeldung.com?key1=value [email protected]!$2&key3=value%3", scheme + "://" + host + "?" + decodedQuery); }
Два важных момента здесь:
- анализ URL-адреса перед декодированием
- используйте одну и ту же схему кодирования для кодирования и декодирования
Если бы мы декодировали, а не анализировали, части URL-адресов могли бы быть проанализированы неправильно. Если бы мы использовали другую схему кодирования для декодирования данных, это привело бы к мусорным данным.
5. Кодирование сегмента пути
URLEncoder не может использоваться для кодирования сегмента пути URL . Компонент Path ссылается на иерархическую структуру, которая представляет путь к каталогу, или служит для поиска ресурсов, разделенных “/” .
Зарезервированные символы в сегменте пути отличаются от значений параметров запроса. Например, знак “+” является допустимым символом в сегменте пути и поэтому не должен кодироваться.
Для кодирования сегмента пути мы используем класс 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 , он возвращал закодированное значение, а + не кодируется, потому что это символ значения в компоненте path.
Давайте добавим переменную пути к нашему тестовому 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 .