Guia para codificação / decodificação de URL Java
1. Introdução
Simplificando,URL encoding traduz os caracteres especiais do URL para uma representação que segue as especificações e pode ser corretamente entendida e interpretada.
Neste artigo, vamos nos concentrar emhow to encode/decode the URL or form data para que ele cumpra as especificações e transmita pela rede corretamente.
2. Analise o URL
Uma sintaxe básicaURI pode ser generalizada como:
scheme:[//[user:[email protected]]host[:port]][/]path[?query][#fragment]
A primeira etapa para codificar um URI é examinar suas partes e depois codificar apenas as partes relevantes.
Vejamos um exemplo de um URI:
String testUrl =
"http://www.example.com?key1=value+1&key2=value%40%21%242&key3=value%253";
Uma maneira de analisar o URI é carregar a representação String para uma classejava.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.example.com"));
assertThat(uri.getRawQuery(),
.is("key1=value+1&key2=value%40%21%242&key3=value%253"));
}
A classeURI analisa o URL de representação da string e expõe suas partes por meio de uma API simples - por exemplo,getXXX.
3. Codifique o URL
Ao codificar o URI, uma das armadilhas comuns é codificar o URI completo. Normalmente, precisamos codificar apenas a parte da consulta do URI.
Vamos codificar os dados usando o métodoencode(data, encodingScheme) da classeURLEncoder:
private String encodeValue(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
}
@Test
public void givenRequestParam_whenUTF8Scheme_thenEncode() throws Exception {
Map requestParams = 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.example.com?", ""));
assertThat(testUrl, is(encodedURL));
O métodoencode aceita dois parâmetros:
-
data - string a ser traduzida
-
encodingScheme - nome da codificação de caracteres
Este métodoencode converte a string no formatoapplication/x-www-form-urlencoded.
O esquema de codificação converterá os caracteres especiais em uma representação hexadecimal de dois dígitos de 8 bits que serão representados na forma de “%xy“. Quando estamos lidando com parâmetros de caminho ou adicionando parâmetros dinâmicos, codificaremos os dados e depois enviaremos para o servidor.
Note: A recomendaçãoWorld Wide Web Consortium afirma queUTF-8 deve ser usado. Não fazer isso pode introduzir incompatibilidades. (Referência:https://docs.oracle.com/javase/7/docs/api/java/net/URLEncoder.html)
4. Decodifique o URL
Vamos agora decodificar o URL anterior usando o método de decodificação deURLDecoder:
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.example.com?key1=value 1&[email protected]!$2&key3=value%3",
scheme + "://" + host + "?" + decodedQuery);
}
Os dois bits importantes aqui são:
-
analisar URL antes de decodificar
-
use o mesmo esquema de codificação para codificação e decodificação
Se decodificássemos do que analisar, as partes da URL podem não ser analisadas corretamente. Se usássemos outro esquema de codificação para decodificar os dados, isso resultaria em dados de lixo.
5. Codificar um segmento de caminho
URLEncoder não pode ser usado para segmento de caminho de codificação deURL. O componente de caminho refere-se à estrutura hierárquica que representa um caminho de diretório ou serve para localizar recursos separados por“/”.
Os caracteres reservados no segmento de caminho são diferentes dos valores dos parâmetros de consulta. Por exemplo, um sinal de "+" é um caractere válido no segmento de caminho e, portanto, não deve ser codificado.
Para codificar o segmento do caminho, usamos a classeUriUtils do Spring Framework. A classeUriUtils fornece os métodosencodePatheencodePathSegment para codificar o caminho e o segmento do caminho, respectivamente.
Vejamos um exemplo:
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);
}
No trecho de código acima, podemos ver que quando usamos o métodoencodePathSegment, ele retornou o valor codificado e + não está sendo codificado porque é um caractere de valor no componente do caminho.
Vamos adicionar uma variável de caminho ao nosso URL de teste:
String testUrl
= "/path+1?key1=value+1&key2=value%40%21%242&key3=value%253";
e para montar e afirmar um URL codificado corretamente, alteremos o teste da seção 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. Conclusão
Neste tutorial, vimos como codificar e decodificar os dados para que possam ser transferidos e interpretados corretamente. Embora o artigo tenha focado na codificação / decodificação dos valores dos parâmetros de consulta do URI, a abordagem também se aplica aos parâmetros do formulário HTML.
Você pode encontrar o código-fonteover on GitHub.