Uma abordagem para padronizar casos de uso de correspondência de padrões comuns.
Contexto
O roteamento é uma peça fundamental de todo aplicativo da Web. Em essência, o roteamento envolve tomar um URL, aplicar a ele alguma correspondência de padrões ou outra lógica específica do app e, em geral, exibir o conteúdo da Web com base no resultado. O roteamento pode ser implementado de várias maneiras: às vezes, é um código sendo executado em um servidor que mapeia um caminho para arquivos no disco ou uma lógica em um app de página única que aguarda mudanças no local atual e cria uma parte correspondente do DOM para exibir.
Embora não haja um padrão definitivo, os desenvolvedores da Web aproveitaram
uma sintaxe comum para expressar padrões de roteamento de URL que compartilham muito em
comum com
regular expressions
,
mas com algumas adições específicas de domínio, como tokens para segmentos de caminho correspondentes.
Frameworks conhecidos do lado do servidor, como
Express e
Ruby on Rails, usam essa sintaxe (ou algo muito próximo), e os desenvolvedores de JavaScript podem usar módulos como
path-to-regexp
ou
regexpparam
para adicionar essa
lógica ao próprio código.
O URLPattern
é uma adição à plataforma da Web baseada na base criada
por esses frameworks. O objetivo é padronizar uma sintaxe de padrão de roteamento,
incluindo suporte a caracteres curinga, grupos de tokens nomeados, grupos de expressões regulares
e modificadores de grupo. As instâncias de URLPattern
criadas com essa sintaxe podem executar tarefas de roteamento comuns, como a correspondência de URLs completos ou de um URL pathname
, além de retornar informações sobre o token e as correspondências de grupo.
Outro benefício de fornecer a correspondência de URL diretamente na plataforma da Web é que uma sintaxe comum pode ser compartilhada com outras APIs que também precisam corresponder a URLs.
Suporte a navegadores e polyfills
URLPattern
está ativado por padrão no Chrome e no Edge 95 ou versões mais recentes.
A biblioteca
urlpattern-polyfill
oferece uma maneira de usar a interface URLPattern
em navegadores
ou ambientes como o Node, que não têm suporte integrado. Se
você usar o polyfill, use a detecção de recursos para garantir que
ele só seja carregado se o ambiente atual não oferecer suporte. Caso contrário,
você vai perder um dos principais benefícios do URLPattern
: o fato de que
os ambientes de suporte não precisam fazer o download e analisar outros códigos para
usá-los.
if (!(globalThis && 'URLPattern' in globalThis)) {
// URLPattern is not available, so the polyfill is needed.
}
Compatibilidade de sintaxe
Um conceito orientador do URLPattern
é evitar a reinvenção. Se você já conhece a sintaxe de roteamento usada no Express ou no Ruby on Rails, não precisa aprender nada novo. No entanto, considerando as pequenas divergências
entre as sintaxes em bibliotecas de roteamento conhecidas, algo precisou ser escolhido como a
sintaxe base, e os designers de URLPattern
decidiram usar a sintaxe de padrão
de path-to-regexp
(mas não a superfície da API) como ponto de partida.
Essa decisão foi tomada após uma consulta detalhada com o mantenedor atual do path-to-regexp
.
A melhor maneira de se familiarizar com o núcleo da sintaxe compatível é
consultar a
documentação de
path-to-regexp
. Você pode
ler a documentação
destinada à publicação no MDN na página inicial
atual do GitHub.
Mais recursos
A sintaxe de URLPattern
é um superconjunto compatível com path-to-regexp
, já que
URLPattern
oferece suporte a um recurso incomum entre as bibliotecas de roteamento: origens
correspondentes,
incluindo caracteres curinga em nomes de host. A maioria das outras bibliotecas de roteamento lida apenas com
o pathname
e, às vezes, a parte de
pesquisa ou
hash de um
URL. Elas nunca precisam verificar a parte de origem de um URL, já que são usadas apenas para roteamento de mesma origem em um app da Web independente.
Considerar as origens abre portas para outros casos de uso, como
roteamento de solicitações de origem cruzada dentro de um
manipulador de eventos fetch
do
service worker. Se você está roteando apenas URLs de mesma origem, pode
ignorar esse recurso adicional e usar URLPattern
como
outras bibliotecas.
Exemplos
Como construir o padrão
Para criar um URLPattern
, transmita o construtor dele strings ou um objeto com propriedades que contenham informações sobre o padrão para correspondência.
Transmitir um objeto oferece o controle mais explícito sobre qual padrão usar para corresponder cada componente de URL. Em sua forma mais detalhada, pode parecer
const p = new URLPattern({
protocol: 'https',
username: '',
password: '',
hostname: 'example.com',
port: '',
pathname: '/foo/:image.jpg',
search: '*',
hash: '*',
});
Fornecer uma string vazia para uma propriedade só terá correspondência se a parte correspondente do URL não estiver definida. O caractere curinga *
corresponde a qualquer valor de uma determinada parte do URL.
O construtor oferece vários atalhos para simplificar o uso. Omitir completamente search
e hash
ou qualquer outra propriedade equivale a defini-las como o caractere curinga '*'
. O exemplo acima pode ser simplificado para
const p = new URLPattern({
protocol: 'https',
username: '',
password: '',
hostname: 'example.com',
port: '',
pathname: '/foo/:image.jpg',
});
Como um atalho extra, todas as informações sobre a origem podem ser
fornecidas em uma única propriedade, baseURL
, levando a
const p = new URLPattern({
pathname: '/foo/:image.jpg',
baseURL: 'https://example.com',
});
Todos esses exemplos pressupõem que seu caso de uso envolve origens correspondentes. Se
você só tiver interesse em fazer a correspondência nas outras partes do URL, excluindo
a origem (como é o caso de muitos cenários de roteamento de origem única
"tradicionais"), omita completamente as informações de origem e forneça
alguma combinação das propriedades pathname
, search
e hash
. Assim como antes,
as propriedades omitidas serão tratadas como se estivessem definidas como o padrão de
caractere curinga *
.
const p = new URLPattern({pathname: '/foo/:image.jpg'});
Como alternativa à transmissão de um objeto para o construtor, você pode fornecer
uma ou duas strings. Se uma string for informada, ela precisará representar um padrão de URL completo, incluindo informações de padrão usadas para corresponder à origem. Se você
fornecer duas strings, a segunda será usada como baseURL
, e a primeira
será considerada relativa a essa base.
Se uma ou duas strings forem fornecidas, o construtor URLPattern
analisará
o padrão de URL completo, dividindo-o em componentes de URL e mapear cada parte
do padrão maior para o componente correspondente. Isso significa que,
em segundo plano, cada URLPattern
criado com strings acaba sendo representado
da mesma forma que um URLPattern
equivalente criado com um objeto. O
construtor de strings é apenas um atalho para quem prefere uma interface
menos detalhada.
const p = new URLPattern('https://example.com/foo/:image.jpg?*#*');
Ao usar strings para criar um URLPattern
, tenha em mente algumas ressalvas.
Deixar de fora uma propriedade ao usar um objeto para construir URLPattern
é equivalente a fornecer um caractere curinga *
para essa propriedade. Quando o padrão de string de URL completo
é analisado, se um dos componentes de URL estiver sem um valor, ele será
tratado como se a propriedade do componente estivesse definida como ''
, o que só vai corresponder
quando esse componente estiver vazio.
Ao usar strings, você precisa incluir explicitamente os caracteres curinga se quiser
que eles sejam usados no URLPattern
construído.
// p1 and p2 are equivalent.
const p1 = new URLPattern('/foo', location.origin);
const p2 = new URLPattern({
protocol: location.protocol,
hostname: location.hostname,
pathname: '/foo',
search: '',
hash: '',
});
// p3 and p4 are equivalent.
const p3 = new URLPattern('/foo?*#*', location.origin);
const p4 = new URLPattern({
protocol: location.protocol,
hostname: location.hostname,
pathname: '/foo',
});
Também é necessário estar ciente de que a análise de um padrão de string nos componentes dele é
possivelmente ambígua. Existem caracteres, como :
, que são encontrados nos URLs,
mas também têm um significado especial na sintaxe de correspondência de padrões. Para evitar essa
ambiguidade, o construtor URLPattern
presume que qualquer um desses caracteres
especiais faz parte de um padrão, não do URL. Se você quiser que um caractere ambíguo seja interpretado como parte do URL, crie um escape com \` character. For example, the literal URL
about:blankshould be escaped as
'about\:blank'` quando fornecido como uma string.
Como usar o padrão
Depois de criar um URLPattern
, você tem duas opções para usá-lo. Os
métodos test()
e exec()
recebem a mesma entrada e usam o mesmo
algoritmo para verificar uma correspondência e diferem apenas no valor de retorno. test()
retorna true
quando há uma correspondência para a entrada especificada. Caso contrário, retorna false
.
exec()
retorna informações detalhadas sobre a correspondência com grupos de captura
ou null
se não houver correspondência. Os exemplos a seguir demonstram o uso de
exec()
, mas é possível trocar test()
por qualquer um deles se você quiser apenas um
valor de retorno booleano simples.
Uma maneira de usar os métodos test()
e exec()
é transmitindo strings.
Assim como o construtor, se uma única string é fornecida, ela
precisa ser um URL completo, incluindo a origem. Se duas strings são fornecidas, a
segunda string é tratada como um valor baseURL
, e a primeira é avaliada
como relativa a essa base.
const p = new URLPattern({
pathname: '/foo/:image.jpg',
baseURL: 'https://example.com',
});
const result = p.exec('https://example.com/foo/cat.jpg');
// result will contain info about the successful match.
// const result = p.exec('/foo/cat.jpg', 'https://example.com')
// is equivalent, using the baseURL syntax.
const noMatchResult = p.exec('https://example.com/bar');
// noMatchResult will be null.
Como alternativa, você pode transmitir o mesmo tipo de objeto aceito pelo construtor, com propriedades definidas apenas para as partes do URL que você quer corresponder.
const p = new URLPattern({pathname: '/foo/:image.jpg'});
const result = p.exec({pathname: '/foo/:image.jpg'});
// result will contain info about the successful match.
Ao usar exec()
em uma URLPattern
que contém caracteres curinga ou tokens, o valor de retorno fornece informações sobre quais eram os valores correspondentes no URL de entrada. Isso pode evitar que você tenha que analisar esses valores por conta própria.
const p = new URLPattern({
hostname: ':subdomain.example.com',
pathname: '/*/:image.jpg'
});
const result = p.exec('https://imagecdn1.example.com/foo/cat.jpg');
// result.hostname.groups.subdomain will be 'imagecdn1'
// result.pathname.groups[0] will be 'foo', corresponding to *
// result.pathname.groups.image will be 'cat'
Grupos anônimos e nomeados
Ao transmitir uma string de URL para exec()
, você recebe um valor que informa quais partes corresponderam a todos os grupos do padrão.
O valor de retorno tem propriedades que correspondem aos componentes do
URLPattern
, como pathname
. Portanto, se um grupo foi definido como parte da
parte pathname
do URLPattern
, as correspondências poderão ser encontradas no
pathname.groups
do valor de retorno. As correspondências são representadas de maneira diferente,
dependendo se o padrão correspondente é um grupo anônimo ou nomeado.
É possível usar índices de matriz para acessar os valores de uma correspondência de padrão anônima.
Se houver vários padrões anônimos, o índice 0
vai representar o valor correspondente
do primeiro padrão à esquerda, com 1
e outros índices usados para padrões
subsequentes.
Ao usar grupos nomeados em um padrão, as correspondências serão expostas como propriedades com nomes que correspondem ao nome de cada grupo.
Suporte a Unicode e normalização
URLPattern
oferece suporte a caracteres Unicode de algumas maneiras diferentes.
Grupos nomeados, como
:café
, podem conter caracteres Unicode. As regras usadas para identificadores JavaScript válidos se aplicam a grupos nomeados.O texto dentro de um padrão será codificado automaticamente de acordo com as mesmas regras usadas para codificação de URL desse componente específico. Os caracteres Unicode em
pathname
serão codificados por porcentagem (link em inglês), de modo que um padrãopathname
como/café
é normalizado para/caf%C3%A9
automaticamente. Os caracteres Unicode nohostname
são codificados automaticamente usando Punycode, em vez de codificação por cento.Os grupos de expressões regulares precisam conter somente caracteres ASCII. A sintaxe de expressão regular dificulta a codificação automática de caracteres Unicode nesses grupos, e isso não é seguro. Se você quiser corresponder um caractere Unicode a um grupo de expressões regulares, será necessário codificá-lo manualmente, como
(caf%C3%A9)
, para corresponder acafé
.
Além de codificar caracteres Unicode, o URLPattern
também executa a normalização de URLs. Por exemplo, a /foo/./bar
no componente pathname
é
recolhida para o /foo/bar
equivalente.
Se tiver dúvida sobre como um determinado padrão de entrada foi normalizado, inspecione a instância URLPattern
criada usando o DevTools do navegador.
Para resumir
A demonstração do Glitch incorporada abaixo ilustra um caso de uso principal de URLPattern
dentro do fetch event handler
de um service worker,
mapeando padrões específicos para funções assíncronas que podem gerar uma
resposta a solicitações de rede. Os conceitos neste exemplo também podem ser aplicados a
outros cenários de roteamento, no lado do servidor ou do cliente.
Feedback e planos futuros
Embora a funcionalidade básica do URLPattern
tenha chegado ao Chrome e ao Edge,
há algumas adições planejadas. Alguns aspectos do URLPattern
ainda estão em desenvolvimento,
e há várias
perguntas em aberto
sobre comportamentos específicos que ainda podem ser refinadas. Recomendamos que você teste
URLPattern
e envie feedback usando um
problema no GitHub.
Suporte a modelos
A biblioteca path-to-regexp
fornece um
compile() function
que inverte efetivamente o comportamento do roteamento. compile()
usa um padrão e valores para os marcadores de posição de token e retorna uma string para um caminho de URL com esses valores substituídos.
Esperamos adicionar isso ao URLPattern no futuro, mas esse não faz parte do escopo da versão inicial.
Possibilitar futuros recursos da plataforma Web
Supondo que URLPattern
se torne uma parte estabelecida da plataforma da Web, outros
recursos que podem se beneficiar da correspondência de roteamento ou padrão podem ser criados
como primitivos.
Há discussões em andamento sobre o uso de URLPattern
para recursos propostos, como correspondência de padrões de escopo de service worker, PWAs como gerenciadores de arquivos e pré-busca especulativa (links em inglês).
Agradecimentos
Confira o documento de explicação original para uma lista completa de confirmações.