Repository navigation
rotas avancadas
Home > Rotas e parâmetros > Rotas avançadas 🚧
Important
Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.
Vale para a 2.0 · Delphi XE em diante e Lazarus/FPC 3.2 em diante · Nível: intermediário
Em uma frase: a rota pode conferir sozinha o tipo e o formato dos parâmetros do caminho, ter parte opcional ou curinga, pertencer a um grupo com prefixo e eventos próprios, atender só certas versões da API e ser escrita com um procedimento anônimo.
Você vai precisar: saber criar uma rota.
Diga o tipo entre < e > logo depois do nome:
Servidor.CreateRoute('clientes/:id<int>', BuscarCliente);-
/clientes/7chega noBuscarCliente. -
/clientes/abcnão casa com essa rota. Se outra rota casar (por exemploclientes/:nome), ela responde; se nenhuma casar, o servidor responde 404. O seu código nunca recebe umidque não é número.
O mesmo vale pelo Object Inspector: em URIParams, mude o ParamType do parâmetro.
Um parâmetro prtString (o padrão) não é conferido: passa qualquer valor, como sempre.
| no texto | ParamType |
aceita |
|---|---|---|
<int> |
prtInteger |
42, -7 (cabe num Int64) |
<number> |
prtNumber |
1.5, -2e3 (ponto decimal, sempre) |
<decimal> |
prtDecimal |
10.25 |
<bool> |
prtBoolean |
true, false, 1, 0
|
<date> |
prtDate |
2026-11-01 (data que existe) |
<time> |
prtTime |
23:59, 23:59:01.250
|
<datetime> |
prtDateTime |
ISO 8601, 2026-11-01T10:20:30Z
|
<base64> |
prtBase64 |
aGVsbG8= |
Junte com : dentro do < >:
Servidor.CreateRoute('produtos/:id<int:range(1,9999)>', Produto);
Servidor.CreateRoute('cupons/:codigo<length(8)>', Cupom);
Servidor.CreateRoute('relatorios/:formato<values(pdf,csv)>', Relatorio);
Servidor.CreateRoute('arquivos/:nome<regex([a-z0-9-]+\.txt)>', Arquivo);| restrição | o que pede | propriedade |
|---|---|---|
min(n), max(n), range(a,b)
|
número entre os limites |
MinValue, MaxValue
|
length(n), length(a,b), minlength(n), maxlength(n)
|
quantidade de caracteres |
MinLength, MaxLength
|
values(a,b,c) |
um dos valores, exato | Values |
regex(...) |
o valor inteiro casa com a expressão | Pattern |
O texto da rota é lido uma vez, quando você o escreve, e preenche o URIParams; o
pedido não reinterpreta nada. Uma restrição desconhecida levanta exceção na hora,
assim como uma expressão regular que não compila.
O Pattern usa o RALRegex, motor próprio do RAL, igual em todas as IDEs (as classes
de regex do Delphi e do FPC são diferentes entre si). A sintaxe é a do RE2: classes
[a-z], \d \w \s \b, ^ $ \A \z, * + ? {n,m} (e as versões preguiçosas com ?),
grupos com e sem captura, grupos com nome (?P<nome>...), (?i) para ignorar
maiúsculas. Ele roda em tempo linear: nenhum texto, por maior que seja, trava o
servidor (o famoso (a+)+$ contra milhares de a responde na hora).
Para isso ele não aceita retrovisor (\1) nem lookahead/lookbehind ((?=,
(?!, (?<=): o padrão levanta ERALRegex ao compilar, com a posição do erro.
Você pode usar o motor no seu código também:
uses RALRegex;
vRe := TRALRegex.Create('(?P<ano>\d{4})-(?P<mes>\d\d)');
try
vMatch := vRe.Match('emitida em 2026-11-01');
if vMatch.Success then
ShowMessage(vMatch.GroupByName('ano')); // 2026
vTexto := vRe.Replace(vTexto, '${mes}/${ano}');
finally
vRe.Free;
end;IsMatch, Match, Matches, Replace ($1, ${nome}, $$) e Split. Um padrão
compilado pode ser usado por várias threads ao mesmo tempo.
Servidor.CreateRoute('pedidos/:status?', ListarPedidos); // /pedidos e /pedidos/abertos
Servidor.CreateRoute('estaticos/*caminho', Arquivos); // /estaticos/css/site.css-
:nome?só no fim da rota. Quando o pedido não traz, o parâmetro não existe (ParamByName('status')devolvenil). -
*nomesó como último pedaço: pega o resto do caminho, com as barras (caminho=css/site.css). Também casa sem nada depois (/estaticos), com valor vazio. - Uma rota exata ganha da opcional e do curinga: com
estaticos/logo.pngeestaticos/*caminho, o pedido/estaticos/logo.pngvai para a primeira.
Um grupo junta rotas com o mesmo prefixo, o mesmo login e os mesmos eventos:
vGrupo := Servidor.RouteGroups.AddGroup('api', '/api/v1');
vGrupo.SkipAuthMethods := [amGET]; // GET público em todo o grupo
vGrupo.Description.Text := 'API pública'; // vira a tag no Swagger
vGrupo.OnBeforeExecute := ConferirChave;
Servidor.CreateRoute('clientes', Clientes).Group := 'api'; // atende /api/v1/clientes- O caminho da rota fica:
Domaindo módulo +Prefixdo grupo + a rota. - Mudou o
Prefix, mudam todas as rotas do grupo. Apagou o grupo, elas voltam ao caminho sem prefixo. - Os módulos também têm
RouteGroups.
A ordem é sempre esta:
-
BeforeExecutedo módulo -
OnBeforeExecutedo grupo -
OnBeforeExecuteda rota - o procedimento da rota
-
OnAfterExecuteda rota -
OnAfterExecutedo grupo -
AfterExecutedo módulo
Se o OnBeforeExecute do grupo ou da rota responder um status 400 ou maior
(AResponse.Answer(403, ...)), o pedido termina ali: a rota não roda. É o lugar de
uma checagem que vale só para parte da API.
Note
Não existe plugin por rota: plugins valem para o servidor inteiro. Para um plugin
agir só em parte dos caminhos, use IncludePaths ou ExcludePaths dele (abaixo).
Todo plugin tem IncludePaths e ExcludePaths, um caminho por linha:
PluginLimites.IncludePaths.Text := '/api'; // só /api e o que está embaixo
PluginLimites.ExcludePaths.Text := '/health' + sLineBreak + '/api/*/publico';-
/apivale para/apie tudo abaixo dele;*vale um pedaço qualquer. - Valem nas fases de validação e de processamento do plugin. A autenticação tem o
SkipAuthMethodsde rota e de grupo.
QUERY é um GET com corpo: seguro e repetível, para uma busca grande demais para a URL.
Servidor.CreateRoute('busca', Buscar).Allow([amQUERY]);
Cliente.Request.AddBody('{"cidade":"Recife","limite":50}', rctAPPLICATIONJSON);
Cliente.Query('/busca', vResposta);- Motores de cliente que enviam
QUERY: Indy, fpHTTP, mORMot2, netHTTP, OkHttp, MsQuic e Kwik. Em outro motor,Querylevanta exceção antes de conectar. - Uma rota
amALLtambém aceitaQUERY. No Swagger (OpenAPI 3.1) oQUERYnão aparece: a especificação só o terá na 3.2.
Servidor.ApiVersioning.Enabled := True;
Servidor.ApiVersioning.Default := '1';
Servidor.ApiVersioning.Deprecated.Text := '1=Sat, 01 Jan 2028 00:00:00 GMT';
Servidor.CreateRoute('clientes', ClientesV1).Versions := '1';
Servidor.CreateRoute('clientes', ClientesV2).Versions := '2,3';Source |
o cliente pede a versão 2 assim |
|---|---|
avPath (padrão) |
GET /v2/clientes (o /v2 sai do caminho: a rota é clientes) |
avHeader |
cabeçalho Api-Version: 2 (HeaderName) |
avQuery |
?api-version=2 (QueryName) |
avMediaType |
Accept: application/vnd.app.v2+json ou Accept: application/json; version=2
|
- Sem versão no pedido, vale
Default. - A versão escolhida fica em
ARequest.ApiVersion. - Rota sem
Versionsusa as do grupo; sem nenhuma, atende qualquer versão. - Uma versão de
Deprecatedrecebe o cabeçalhoDeprecation: truee, se tiver data depois do=,Sunsetcom ela. - Pedido de uma versão que nenhuma rota daquele caminho atende: 404.
O NGINX manda https://empresa.com/minhaapp/clientes para o seu servidor sem tirar
o /minhaapp:
Servidor.PathBase := '/minhaapp';- O
/minhaappsai do caminho antes de tudo: rotas, plugins e módulos veem/clientes. - Pedido fora do
/minhaapp: 404. - Um
Locationrelativo que a sua rota responde (/novo) sai como/minhaapp/novo.
No Delphi (e no FPC 3.3.1), a rota pode ser escrita ali mesmo:
uses RALRoutesAnon;
Servidor.Get('/ping',
procedure(ARequest: TRALRequest; AResponse: TRALResponse)
begin
AResponse.Answer(HTTP_OK, 'pong', rctTEXTPLAIN);
end);Get, Post, Put, Patch, Delete e Map(rota, [métodos], ...), no servidor e
nos módulos; todos devolvem a rota, para encadear. No Lazarus com FPC 3.2 a unit
compila vazia: use o CreateRoute de sempre.
Com 16 rotas ou mais num servidor ou módulo, o RAL procura a rota por uma árvore dos pedaços do caminho em vez de testar uma por uma. Você não precisa fazer nada, e a escolha é a mesma: entre rotas que casam, ganha a de menor peso e, empatando, a que foi criada primeiro.
Nos Samples da 2.0: Delphi/Console/RoutingExample (Indy) e
Lazarus/Console/RoutingExample (fpHTTP), com tudo desta página num servidor só.
| sintoma | causa | solução |
|---|---|---|
404 num /clientes/abc
|
a rota é :id<int>
|
é o esperado; crie outra rota para texto, se quiser |
Unknown route constraint ao criar a rota |
nome errado dentro do < >
|
veja a tabela de restrições |
ERALRegex ao criar a rota |
\1, (?=, (?<= ou expressão mal formada |
reescreva sem retrovisor nem lookaround |
The QUERY method is not supported by the ... engine |
o motor do cliente não envia QUERY
|
troque o motor (veja a lista acima) |
tudo 404 depois de ligar o PathBase
|
o proxy já tira o prefixo | deixe PathBase vazio |
| rota de grupo não atende |
Group com o nome errado |
o nome é o de RouteGroups, sem diferença de maiúsculas |
Começar
Servidor
- Rotas e parâmetros
- Rotas avançadas: tipos, grupos, versões 🚧
- Ler o pedido e responder
- Resposta em fluxo 🚧
- Plugins 🚧
- Autenticação
- Banco de dados (DBWare)
- Servir site e arquivos
- Swagger e Postman
- Servidor SOAP 🚧
- HTTPS
- Criptografia ponto a ponto
- Segurança para produção
- Colocar no ar
- Desempenho
MQTT 🚧
- O que é e quando usar 🚧
- Cliente MQTT 🚧
- Broker MQTT 🚧
- MQTT-SN 🚧
- Ponte REST 🚧
- Segurança 🚧
- Rede ruim 🚧
Cliente
Motores
Avançado
Ajuda
- Problemas comuns
- Atualizar o Indy
- Várias versões do Delphi
- Vídeos e guias
- Módulos de terceiros
- Licenças
- Contribuir
- Roadmap e changelog
🚧 = em desenvolvimento, pode mudar