URL Encode/Decode: por que existem duas funções diferentes para "a mesma coisa"
Codificação de URL (percent-encoding, definida pela RFC 3986) existe
porque uma URL só pode conter um conjunto restrito de caracteres
seguros — qualquer coisa fora desse conjunto (espaço, acentos,
caracteres reservados usados fora de sua função sintática, como
& dentro do valor de um parâmetro) precisa ser
representada como % seguido do valor hexadecimal do byte
correspondente. Um espaço vira %20, um "ã" vira
%C3%A3 (dois bytes em UTF-8, cada um percent-encoded
separadamente) — a mesma lógica de múltiplos bytes por caractere já
discutida na página de Texto ↔ Binário
deste site.
encodeURIComponent vs. encodeURI: a distinção que causa bugs reais
JavaScript oferece duas funções de codificação com propósitos
diferentes, frequentemente confundidas: encodeURIComponent()
codifica tudo exceto um conjunto mínimo de caracteres
sempre seguros — pensada para codificar um único valor
que vai dentro de uma URL maior (um parâmetro de query string, por
exemplo), por isso codifica até caracteres como &,
= e /, que teriam significado estrutural se
não fossem escapados. encodeURI() preserva os caracteres
estruturais de uma URL completa (:, /,
?, &, =) intencionalmente,
porque é pensada para codificar uma URL inteira, não um valor isolado.
Usar encodeURI() para codificar um parâmetro individual é
um bug clássico: se o próprio valor do parâmetro contiver
& (por exemplo, buscar pelo texto "rock & roll"),
esse caractere não seria escapado, e o parser da URL de destino
interpretaria incorretamente onde um parâmetro termina e outro começa.
Por que testar com caracteres reservados é o caso de teste real
Testar um codificador de URL só com texto simples (letras e números)
nunca expõe esse tipo de bug, porque nenhum desses caracteres exige
codificação em nenhuma das duas funções. O caso de teste que
efetivamente diferencia as duas funções — e expõe qual delas está
sendo usada incorretamente num sistema — é especificamente um valor
contendo &, =, # ou
+ (que tem significado especial adicional: representa
espaço em query strings por convenção histórica, mas não em outras
partes da URL).