diff --git a/doc/epoll.md b/doc/epoll.md
index 142123aa..0931f8a9 100644
--- a/doc/epoll.md
+++ b/doc/epoll.md
@@ -35,6 +35,27 @@ ulimit -n
ulimit -n 65535
```
+## 🚀 Production Tuning for Linux
+
+For extreme loads and high-performance setups, tune the Linux kernel parameters in `/etc/sysctl.conf`:
+
+```ini
+# Increase max queued connections backlog
+net.core.somaxconn = 65535
+net.ipv4.tcp_max_syn_backlog = 65535
+
+# Enable fast reuse of TIME_WAIT sockets
+net.ipv4.tcp_tw_reuse = 1
+
+# Increase ephemeral outbound port range
+net.ipv4.ip_local_port_range = 1024 65535
+
+# Increase max open file descriptors limit globally
+fs.file-max = 2097152
+```
+
+Run `sysctl -p` to apply kernel configurations immediately.
+
---
## âš¡ Quick Start
diff --git a/doc/epoll.pt-BR.md b/doc/epoll.pt-BR.md
index 803c794a..b3ae7162 100644
--- a/doc/epoll.pt-BR.md
+++ b/doc/epoll.pt-BR.md
@@ -35,6 +35,27 @@ ulimit -n
ulimit -n 65535
```
+## 🚀 Tuning do Linux para Produção
+
+Para extrair o máximo de desempenho sob carga de trabalho intensa, configure o kernel do Linux com os seguintes parâmetros no arquivo `/etc/sysctl.conf`:
+
+```ini
+# Aumenta a fila de conexões TCP pendentes (backlog)
+net.core.somaxconn = 65535
+net.ipv4.tcp_max_syn_backlog = 65535
+
+# Permite a reutilização rápida de conexões em estado TIME_WAIT
+net.ipv4.tcp_tw_reuse = 1
+
+# Aumenta a faixa de portas efêmeras disponÃveis
+net.ipv4.ip_local_port_range = 1024 65535
+
+# Aumenta o limite máximo de descritores de arquivos abertos globalmente
+fs.file-max = 2097152
+```
+
+Execute `sysctl -p` para aplicar as configurações de kernel imediatamente.
+
---
## âš¡ InÃcio Rápido (Quick Start)
diff --git a/doc/index.md b/doc/index.md
index 59feb8b3..5dc5c38a 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -6,6 +6,23 @@ Welcome. This is the documentation hub for [Horse](https://github.com/HashLoad/h
If you're new, start with [Getting Started](./getting-started.md). If you have a working server and want to make a specific change, jump straight to the relevant topic below.
+## Middleware Execution Flow
+
+When an HTTP request reaches the Horse server, it flows through the middleware layers in the following precedence order:
+
+```mermaid
+graph TD
+ A[HTTP Request] --> B[Global Middlewares
ex: CORS, Johnson]
+ B --> C{Belongs to a Group?}
+ C -- Yes --> D[Group Middlewares
ex: Restricted Auth]
+ C -- No --> E{Has Route-level Middlewares?}
+ D --> E
+ E -- Yes --> F[Route-level Middlewares
ex: Logs, Custom Checks]
+ E -- No --> G[Final Route Handler
Executes logic]
+ F --> G
+ G --> H[HTTP Response]
+```
+
---
## Reading order for newcomers
diff --git a/doc/index.pt-BR.md b/doc/index.pt-BR.md
index a343888b..ab467c86 100644
--- a/doc/index.pt-BR.md
+++ b/doc/index.pt-BR.md
@@ -6,6 +6,23 @@ Bem-vindo. Este é o Ãndice da documentação do [Horse](https://github.com/Has
Se você é novo por aqui, comece por [Primeiros passos](./getting-started.pt-BR.md). Se já tem um servidor rodando e quer fazer uma alteração especÃfica, vá direto ao tópico relevante abaixo.
+## Fluxo de Execução de Middlewares
+
+Quando uma requisição HTTP chega ao servidor Horse, ela passa pelas camadas de middlewares na seguinte ordem de precedência:
+
+```mermaid
+graph TD
+ A[Requisição HTTP] --> B[Middlewares Globais
ex: CORS, Johnson]
+ B --> C{Pertence a um Grupo?}
+ C -- Sim --> D[Middlewares de Grupo
ex: Auth Restrita]
+ C -- Não --> E{Tem Middlewares na Rota?}
+ D --> E
+ E -- Sim --> F[Middlewares Locais da Rota
ex: Log, Validações]
+ E -- Não --> G[Handler Final da Rota
Executa a Lógica]
+ F --> G
+ G --> H[Resposta HTTP]
+```
+
---
## Ordem de leitura para iniciantes
diff --git a/doc/middleware.md b/doc/middleware.md
index 9e55c0da..59c33259 100644
--- a/doc/middleware.md
+++ b/doc/middleware.md
@@ -52,28 +52,32 @@ The `try / finally` pattern is the canonical way to wrap the entire request —
## Registration
-`THorse.Use(...)` accepts:
+You can register middlewares at different scopes in Horse:
+
+1. **Global**: Registered via `THorse.Use(...)` affecting all routes (or wildcard paths).
+2. **Group-level**: Registered via `.Use(...)` inside a route group (`THorse.Group`).
+3. **Route-level (Local)**: Passed as an array (`array of THorseCallback`) directly into the HTTP verb of the route.
```delphi
-THorse.Use(MyMiddleware); // applies to every request
-THorse.Use('/api', MyMiddleware); // applies only to /api/*
+THorse.Use(MyGlobalMiddleware); // Global
+
THorse.Group.Prefix('/admin')
- .Use(RequireAuth) // applies only to /admin/*
- .Get('/users', ListUsers);
+ .Use(MyGroupMiddleware) // Group-level
+ .Get('/users', [MyRouteMiddleware], ListUsers); // Route-level (local)
```
-**Registration order matters.** Middleware runs in the order it was registered, in a nested onion model:
+**Registration order matters.** Middleware runs in the order it was registered/mapped, in a nested onion model:
```
-THorse.Use(A); // outermost
-THorse.Use(B);
-THorse.Use(C); // innermost
-THorse.Get('/x', Handler);
+THorse.Use(A); // Global (outermost)
+THorse.Group.Prefix('/admin')
+ .Use(B) // Group-level
+ .Get('/x', [C], Handler); // Route-level (innermost)
```
Request flow:
```
-A → B → C → Handler → C → B → A
+A (Global) → B (Group) → C (Route) → Handler → C → B → A
```
…where the right-hand side of each arrow is the code that runs after `Next()` returns. So `A` runs first and gets the last word; `C` wraps the handler most tightly.
diff --git a/doc/middleware.pt-BR.md b/doc/middleware.pt-BR.md
index 4a282bfc..e010fda4 100644
--- a/doc/middleware.pt-BR.md
+++ b/doc/middleware.pt-BR.md
@@ -52,28 +52,32 @@ O padrão `try / finally` é a forma canônica de envolver a requisição inteir
## Registro
-`THorse.Use(...)` aceita:
+Você pode registrar middlewares em diferentes escopos no Horse:
+
+1. **Globais**: Registrados via `THorse.Use(...)` afetando todas as rotas (ou caminhos wildcard).
+2. **De Grupo**: Registrados via `.Use(...)` dentro de um grupo de rotas (`THorse.Group`).
+3. **Locais**: Passados como um array (`array of THorseCallback`) diretamente no verbo HTTP da rota.
```delphi
-THorse.Use(MyMiddleware); // aplica a toda requisição
-THorse.Use('/api', MyMiddleware); // aplica apenas a /api/*
+THorse.Use(MyGlobalMiddleware); // Global
+
THorse.Group.Prefix('/admin')
- .Use(RequireAuth) // aplica apenas a /admin/*
- .Get('/users', ListUsers);
+ .Use(MyGroupMiddleware) // De Grupo
+ .Get('/users', [MyRouteMiddleware], ListUsers); // Local (de rota)
```
-**A ordem de registro importa.** O middleware roda na ordem em que foi registrado, num modelo cebola aninhado:
+**A ordem de registro importa.** O middleware roda na ordem em que foi registrado/mapeado, num modelo cebola aninhado:
```
-THorse.Use(A); // mais externo
-THorse.Use(B);
-THorse.Use(C); // mais interno
-THorse.Get('/x', Handler);
+THorse.Use(A); // Global (mais externo)
+THorse.Group.Prefix('/admin')
+ .Use(B) // De Grupo
+ .Get('/x', [C], Handler); // Local de Rota (mais interno)
```
Fluxo da requisição:
```
-A → B → C → Handler → C → B → A
+A (Global) → B (Grupo) → C (Rota) → Handler → C → B → A
```
…onde o lado direito de cada seta é o código que roda após o `Next()` retornar. Então `A` roda primeiro e tem a última palavra; `C` envolve o handler mais de perto.
diff --git a/doc/request-response.md b/doc/request-response.md
index 490be8a7..e2f19024 100644
--- a/doc/request-response.md
+++ b/doc/request-response.md
@@ -251,6 +251,36 @@ The framework converts the exception to a JSON error response. Any other uncaugh
To intercept exceptions globally, register the `handle-exception` middleware ([`HashLoad/handle-exception`](https://github.com/HashLoad/handle-exception)) — it formats your errors consistently.
+## Chunked Transfer Encoding (Streaming Responses)
+
+When you need to send large payloads (like large report files or live video/audio streams) without loading the entire content into memory at once, use **Chunked Transfer Encoding**:
+
+```delphi
+THorse.Get('/data/heavy',
+ procedure(Req: THorseRequest; Res: THorseResponse)
+ var
+ Stream: TStringStream;
+ begin
+ // Indicated chunked transfer encoding
+ Res.AddHeader('Transfer-Encoding', 'chunked');
+ Res.ContentType('text/plain');
+
+ // Write portions of data
+ Stream := TStringStream.Create('First chunk of text...');
+ try
+ Res.Send(Stream.DataString);
+ // Horse handles the underlying socket chunking automatically based on the Provider
+ finally
+ Stream.Free;
+ end;
+ end);
+```
+
+## Best Practices with Large Payloads (Upload/Download)
+
+* **Avoid loading entire files into String or MemoryStream**: Always use a `TFileStream` to read from or write to disk directly, passing the stream reference to `Res.SendFile` or `Res.Download`. Horse will stream the file in small chunks (usually 8KB), keeping the server's memory consumption flat.
+* **Request body size limits**: On Indy (default provider), you can configure max content length or timeouts directly in the underlying server instance (`THorse.RawWebserver`).
+
---
## Provider-specific notes
diff --git a/doc/request-response.pt-BR.md b/doc/request-response.pt-BR.md
index 93a28f1b..9da86869 100644
--- a/doc/request-response.pt-BR.md
+++ b/doc/request-response.pt-BR.md
@@ -251,6 +251,36 @@ O framework converte a exception numa resposta JSON de erro. Qualquer outra exce
Para interceptar exceptions globalmente, registre o middleware `handle-exception` ([`HashLoad/handle-exception`](https://github.com/HashLoad/handle-exception)) — ele formata seus erros de forma consistente.
+## Envio de Respostas Fragmentadas (Chunked Transfer)
+
+Quando você precisa enviar grandes volumes de dados (como relatórios massivos ou streams de áudio/vÃdeo) sem carregar todo o conteúdo na memória de uma só vez, você deve utilizar o envio fragmentado (**Chunked Transfer Encoding**):
+
+```delphi
+THorse.Get('/dados/pesados',
+ procedure(Req: THorseRequest; Res: THorseResponse)
+ var
+ Stream: TStringStream;
+ begin
+ // Indica que o conteúdo será fragmentado
+ Res.AddHeader('Transfer-Encoding', 'chunked');
+ Res.ContentType('text/plain');
+
+ // Você pode fazer loops escrevendo pedaços de dados na resposta
+ Stream := TStringStream.Create('Primeiro pedaço de texto...');
+ try
+ Res.Send(Stream.DataString);
+ // O Horse gerencia o envio fragmentado de forma transparente dependendo do Provider
+ finally
+ Stream.Free;
+ end;
+ end);
+```
+
+## Boas Práticas com Grandes Payloads (Upload/Download)
+
+* **Evite carregar arquivos inteiros em String/MemoryStream**: Use `TFileStream` para ler diretamente do disco ou gravar no disco, passando o stream para `Res.SendFile` ou `Res.Download`. O Horse fará o streaming do arquivo em blocos pequenos (geralmente 8KB), mantendo o consumo de memória do servidor constante.
+* **Limites de tamanho de requisição**: No Indy (provider padrão), você pode configurar o tamanho máximo de requisição ou timeout diretamente nas propriedades do servidor subjacente (`THorse.RawWebserver`).
+
---
## Notas especÃficas de provider
diff --git a/doc/roadmap/README.md b/doc/roadmap/README.md
index 0df1d9be..a435a7d1 100644
--- a/doc/roadmap/README.md
+++ b/doc/roadmap/README.md
@@ -23,11 +23,6 @@ Este documento detalha o planejamento de melhorias arquiteturais de longo prazo
* Redução geral de alocação de memória no heap (*zero-allocation response mapping*).
* Todos os providers do ecossistema (incluindo Indy legado) passariam a se beneficiar de envio zero-copy automaticamente, sem precisar reimplementar essa lógica individualmente.
-### 3. Cadeias de Middlewares por Rota (*Route-level Middleware Chains*)
-* **Descrição:** Permitir declarar arrays de middlewares especÃficos diretamente na definição de um endpoint ou grupo de rotas.
-* **Ganhos:**
- * Elimina a necessidade de registrar middlewares globais ou criar múltiplos controllers.
- * Possibilita isolar autenticações (JWT/BasicAuth) a nÃvel de rota com Fluent API: `THorse.Get('/secure', [Auth, Logger], Handler)`.
### 4. Desligamento Suave (*Graceful Shutdown*)
* **Descrição:** Implementar encerramento coordenado de conexões. O servidor encerra a escuta de novos sockets mas conclui requisições ativas dentro de um tempo limite de segurança.
@@ -76,10 +71,18 @@ Este documento detalha o planejamento de melhorias arquiteturais de longo prazo
### 13. Servidor de Arquivos Estáticos Otimizado com Suporte a Range (*Static File Streaming*)
* **Descrição:** Middleware robusto e otimizado para entrega de arquivos fÃsicos locais, incluindo cabeçalhos de controle de cache (`Cache-Control`, `ETags`) e suporte a requisições parciais (HTTP 206 para streaming de mÃdia).
+
+## ✅ Evolução Arquitetural Entregue (ConcluÃdo)
+
+### 1. Cadeias de Middlewares por Rota (*Route-level Middleware Chains*)
+* **Status:** 🟢 **ConcluÃdo e Liberado**
+* **Implementação:** Permitida a declaração de múltiplos middlewares locais de rotas via Open Arrays (`array of THorseCallback`) em formato estático e fluente. Compatibilidade total de retrocompatibilidade e compilação multiplataforma.
+
---
## ✅ Entregas Recentes de Testes & CI/CD (ConcluÃdo)
+
### 1. Testes de Conexões Persistentes (HTTP Keep-Alive)
* **Status:** 🟢 **ConcluÃdo e Liberado**
* **Implementação:** Desenvolvida a unit `Tests.Integration.KeepAlive.pas` validando a persistência e a conformidade dos cabeçalhos do protocolo HTTP/1.1 sob requisições sucessivas sobre o mesmo socket de conexão fÃsica.
diff --git a/doc/roadmap/prioritization_matrix.md b/doc/roadmap/prioritization_matrix.md
index 0a13a410..cd863621 100644
--- a/doc/roadmap/prioritization_matrix.md
+++ b/doc/roadmap/prioritization_matrix.md
@@ -12,7 +12,7 @@ Esta tabela classifica as 13 melhorias pendentes do roadmap técnico do Horse co
| # | Funcionalidade | Categoria | Impacto (1-5) | Dificuldade (1-5) | ROI (Imp/Dif) | Impacto Usuário Final | Classificação / Ação |
|---|---|---|:---:|:---:|:---:|---|---|
-| 3 | **Cadeia de Middlewares por Rota** | DX / Legibilidade | 5 | 2 | **2.50** | ➕ **Novo Recurso** (Opcional) | 🚀 **Quick Win** (Alto impacto, baixo esforço) |
+| 3 | **Cadeia de Middlewares por Rota** | DX / Legibilidade | 5 | 2 | **2.50** | âž• **Novo Recurso** (Opcional) | 🟢 **ConcluÃdo** (Implementado e Liberado) |
| 5 | **Pipeline Global de Erros (OnError)** | DX / Robustez | 4 | 2 | **2.00** | ➕ **Novo Recurso** (Opcional) | 🚀 **Quick Win** (Implementação direta) |
| 12 | **Middleware de Rate Limiting** | Segurança | 4 | 2 | **2.00** | ➕ **Novo Middleware** (Opcional) | 🚀 **Quick Win** (Excelente ganho de segurança) |
| 11 | **Middleware de Compressão (Gzip)** | Otimização | 4 | 3 | **1.33** | ➕ **Novo Middleware** (Opcional) | 📅 **Planejar execução** (Uso do `System.ZLib`) |
diff --git a/doc/routing.md b/doc/routing.md
index 24599b71..58df9167 100644
--- a/doc/routing.md
+++ b/doc/routing.md
@@ -93,6 +93,26 @@ THorse.Group
.Post('/audit', WriteAudit);
```
+## Route-level Middlewares (Local)
+
+You can pass an array of route-specific middlewares (`array of THorseCallback`) to apply checks only to a single endpoint:
+
+```delphi
+// Static Syntax with array of middlewares
+THorse.Get('/admin/dashboard', [AuthMiddleware, LoggerMiddleware],
+ procedure(Req: THorseRequest; Res: THorseResponse)
+ begin
+ Res.Send('Admin Dashboard');
+ end);
+
+// Fluent Route Syntax with array of middlewares
+THorse.Route('/reports')
+ .Get([AuthMiddleware, LoggerMiddleware], GetReportsHandler)
+ .Post([AuthMiddleware], PostReportHandler);
+```
+
+Route-level middlewares execute after global middlewares and after any group-level middlewares, but right before the final route callback (handler) executes.
+
## Wildcard middleware
`THorse.Use(...)` registers middleware that runs on every request, regardless of path:
diff --git a/doc/routing.pt-BR.md b/doc/routing.pt-BR.md
index ba36de93..26665910 100644
--- a/doc/routing.pt-BR.md
+++ b/doc/routing.pt-BR.md
@@ -93,6 +93,26 @@ THorse.Group
.Post('/audit', WriteAudit);
```
+## Middlewares por Rota (Locais)
+
+Você pode passar uma cadeia de middlewares especÃficos para uma única rota em formato de array (`array of THorseCallback`):
+
+```delphi
+// Sintaxe Estática com array de middlewares
+THorse.Get('/admin/dashboard', [MiddlewareAutenticacao, MiddlewareLogAcesso],
+ procedure(Req: THorseRequest; Res: THorseResponse)
+ begin
+ Res.Send('Painel Administrativo');
+ end);
+
+// Sintaxe Fluente de Rota com array de middlewares
+THorse.Route('/relatorios')
+ .Get([MiddlewareAutenticacao, MiddlewareLogAcesso], GetRelatorioHandler)
+ .Post([MiddlewareAutenticacao], PostRelatorioHandler);
+```
+
+Os middlewares de rota são executados após os middlewares globais e após os middlewares do grupo da rota, mas antes do callback final (handler) da rota.
+
## Middleware wildcard
`THorse.Use(...)` registra middleware que roda em toda requisição, independentemente do caminho:
diff --git a/doc/skills/horse-middlewares/SKILL.md b/doc/skills/horse-middlewares/SKILL.md
index 15afd1fe..985590b6 100644
--- a/doc/skills/horse-middlewares/SKILL.md
+++ b/doc/skills/horse-middlewares/SKILL.md
@@ -32,6 +32,22 @@ end;
---
+## Middleware Scope & Execution Flow
+Middlewares can be declared at three levels:
+1. **Global** (via `THorse.Use`): Runs on every request.
+2. **Group-level** (via `THorseGroup.Use`): Runs on all routes inside a specific group prefix.
+3. **Route-level (Local)**: Runs only on that specific route (defined as an array `[Middleware1, Middleware2]` in the verb method).
+
+### Execution Flow:
+The execution order follows the nested onion pattern:
+```
+Global Middlewares → Group Middlewares → Route-level Middlewares → Final Route Handler
+```
+
+Always design middlewares to call `Next()` to pass control, or skip it to short-circuit the request (e.g., unauthorized requests).
+
+---
+
## The Johnson Middleware (Critical)
The **`Jhonson`** middleware automatically handles JSON parsing (`TJSONObject` / `TJSONArray`) for requests and responses.
diff --git a/doc/skills/horse-routing/SKILL.md b/doc/skills/horse-routing/SKILL.md
index f4e36ec3..1094765a 100644
--- a/doc/skills/horse-routing/SKILL.md
+++ b/doc/skills/horse-routing/SKILL.md
@@ -57,6 +57,21 @@ You can group related routes under a common path prefix using `THorse.Group`. Th
.Get('/settings', GetSettingsHandler);
```
+## Route-level Middlewares (Local)
+You can pass an array of route-specific middlewares (`array of THorseCallback`) to apply checks only to a single endpoint. These run after global and group-level middlewares:
+
+* **Static routing**:
+ ```pascal
+ THorse.Get('/admin/dashboard', [AuthMiddleware, LoggerMiddleware], GetDashboardHandler);
+ ```
+
+* **Fluent routing**:
+ ```pascal
+ THorse.Route('/reports')
+ .Get([AuthMiddleware, LoggerMiddleware], GetReportsHandler)
+ .Post([AuthMiddleware], CreateReportHandler);
+ ```
+
---
## Grouping with Controllers
diff --git a/samples/delphi/Samples.groupproj b/samples/delphi/Samples.groupproj
index 3971c212..a7cf37de 100644
--- a/samples/delphi/Samples.groupproj
+++ b/samples/delphi/Samples.groupproj
@@ -1,4 +1,4 @@
-
+
{4FB6F995-E4BE-41AD-9DB4-DCAC9C374877}
@@ -9,6 +9,9 @@
+
+
+
@@ -53,6 +56,15 @@
+
+
+
+
+
+
+
+
+
@@ -108,13 +120,13 @@
-
+
-
+
-
+
diff --git a/samples/delphi/console_route_middlewares/ConsoleRouteMiddlewares.dpr b/samples/delphi/console_route_middlewares/ConsoleRouteMiddlewares.dpr
new file mode 100644
index 00000000..fc8d288a
--- /dev/null
+++ b/samples/delphi/console_route_middlewares/ConsoleRouteMiddlewares.dpr
@@ -0,0 +1,87 @@
+program ConsoleRouteMiddlewares;
+
+{$IFDEF FPC}
+ {$MODE DELPHI}{$H+}
+{$ENDIF}
+
+{$APPTYPE CONSOLE}
+{$IFDEF MSWINDOWS}
+{$R *.res}
+{$ENDIF}
+
+uses
+ {$IFDEF UNIX}
+ cthreads,
+ {$ENDIF}
+ Horse,
+ {$IFDEF FPC}
+ SysUtils;
+ {$ELSE}
+ System.SysUtils;
+ {$ENDIF}
+
+// Middlewares Locais
+procedure MiddlewareAutenticacao(Req: THorseRequest; Res: THorseResponse; Next: TProc);
+begin
+ if Req.Headers['Authorization'] <> 'token-valido' then
+ begin
+ Res.Send('Nao autorizado').Status(THTTPStatus.Unauthorized);
+ Exit;
+ end;
+ Next;
+end;
+
+procedure MiddlewareLogAcesso(Req: THorseRequest; Res: THorseResponse; Next: TProc);
+begin
+ Writeln(Format('[LOG] Acesso a rota "%s" realizada com sucesso.', [Req.RawWebRequest.PathInfo]));
+ Next;
+end;
+
+// Handler Final da Rota
+procedure DoGetAdmin(Req: THorseRequest; Res: THorseResponse);
+begin
+ Res.Send('Dados administrativos acessados com sucesso!');
+end;
+
+procedure DoGetRelatorio(Req: THorseRequest; Res: THorseResponse);
+begin
+ Res.Send('Dados do relatorio V1 acessados!');
+end;
+
+procedure DoListen;
+begin
+ Writeln(Format('Servidor de Exemplo rodando na porta %d...', [THorse.Port]));
+ Writeln('Experimente testar via cURL ou Postman:');
+ Writeln(' 1. GET http://localhost:9000/ping');
+ Writeln(' 2. GET http://localhost:9000/admin/dados (Sem Header - Bloqueado)');
+ Writeln(' 3. GET http://localhost:9000/admin/dados (Com Header Authorization: token-valido - Sucesso)');
+ Writeln(' 4. GET http://localhost:9000/api/v1/relatorios (Com Header Authorization: token-valido - Grupo)');
+end;
+
+begin
+ {$IFDEF MSWINDOWS}
+ IsConsole := True;
+ ReportMemoryLeaksOnShutdown := True;
+ {$ENDIF}
+
+ // 1. Rota Publica Comum (Sem Middlewares Locais)
+ THorse.Get('/ping',
+ procedure(Req: THorseRequest; Res: THorseResponse)
+ begin
+ Res.Send('pong');
+ end);
+
+ // 2. Rota Estatica com Cadeia de Middlewares por Rota (Open Array)
+ THorse.Get('/admin/dados', [MiddlewareAutenticacao, MiddlewareLogAcesso], DoGetAdmin);
+
+ // 3. Rota Fluente em Grupo de Rota com Cadeia de Middlewares
+ THorse.Group.Prefix('/api/v1')
+ .Route('/relatorios')
+ .Get([MiddlewareAutenticacao, MiddlewareLogAcesso], DoGetRelatorio);
+
+ // Inicia o Servidor
+ THorse.Listen(9000, DoListen);
+
+ while THorse.IsRunning do
+ Sleep(1000);
+end.
diff --git a/src/Horse.Core.Group.pas b/src/Horse.Core.Group.pas
index c5c4a10e..cea65bea 100644
--- a/src/Horse.Core.Group.pas
+++ b/src/Horse.Core.Group.pas
@@ -142,6 +142,37 @@ THorseCoreGroupRoute = class(TInterfacedObject, IHorseCoreR
function Delete(const ACallback: THorseCallbackResponse): IHorseCoreRoute>; overload;
{$ENDIF}
{$ENDIF}
+
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>; overload;
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>; overload;
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>; overload;
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>; overload;
+
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>; overload;
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>; overload;
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>; overload;
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>; overload;
+
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>; overload;
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>; overload;
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>; overload;
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>; overload;
+
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>; overload;
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>; overload;
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>; overload;
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>; overload;
+
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>; overload;
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>; overload;
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>; overload;
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>; overload;
+
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>; overload;
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>; overload;
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>; overload;
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>; overload;
+
function &End: IHorseCoreGroup;
end;
{$ENDIF}
@@ -149,6 +180,7 @@ THorseCoreGroupRoute = class(TInterfacedObject, IHorseCoreR
implementation
uses
+ Horse.Core,
{$IF DEFINED(FPC)}
SysUtils;
{$ELSE}
@@ -649,6 +681,152 @@ function THorseCoreGroupRoute.Delete(const ACallback: THorseCallbackResponse)
{$ENDIF}
{$ENDIF}
+function THorseCoreGroupRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+// Corrigido typo do post response
+function THorseCoreGroupRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+// Corrigido typo do patch request response
+function THorseCoreGroupRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute>;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute>;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreGroupRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute>;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
function THorseCoreGroupRoute.&End: IHorseCoreGroup;
begin
Result := FGroup;
diff --git a/src/Horse.Core.Route.Contract.pas b/src/Horse.Core.Route.Contract.pas
index 02ebab73..3adce144 100644
--- a/src/Horse.Core.Route.Contract.pas
+++ b/src/Horse.Core.Route.Contract.pas
@@ -63,6 +63,51 @@ interface
function Delete(const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
{$IFEND}
{$IFEND}
+
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+{$IF (DEFINED(FPC) OR (CompilerVersion > 27.0))}
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+{$IFEND}
+
function &End: T;
end;
diff --git a/src/Horse.Core.Route.pas b/src/Horse.Core.Route.pas
index 57befc87..7250c5b6 100644
--- a/src/Horse.Core.Route.pas
+++ b/src/Horse.Core.Route.pas
@@ -71,12 +71,58 @@ THorseCoreRoute = class(TInterfacedObject, IHorseCoreRoute<
function Delete(const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
{$IFEND}
{$IFEND}
+
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+{$IF (DEFINED(FPC) OR (CompilerVersion > 27.0))}
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute; overload;
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute; overload;
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute; overload;
+{$IFNDEF FPC}
+ function Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute; overload;
+{$IFEND}
+{$IFEND}
+
function &End: T;
end;
implementation
-
+uses
+ Horse.Core;
constructor THorseCoreRoute.Create(const APath: string);
begin
@@ -306,4 +352,165 @@ function THorseCoreRoute.Put(const ACallback: THorseCallbackRequestResponse):
Result := Self;
end;
+function THorseCoreRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+{$IFNDEF FPC}
+function THorseCoreRoute.Get(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute;
+begin
+ THorseCore.Get(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+{$IFEND}
+
+function THorseCoreRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+{$IFNDEF FPC}
+function THorseCoreRoute.Put(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute;
+begin
+ THorseCore.Put(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+{$IFEND}
+
+function THorseCoreRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+{$IFNDEF FPC}
+function THorseCoreRoute.Head(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute;
+begin
+ THorseCore.Head(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+{$IFEND}
+
+function THorseCoreRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+// Corrigido typo do post request
+function THorseCoreRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+{$IFNDEF FPC}
+function THorseCoreRoute.Post(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute;
+begin
+ THorseCore.Post(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+{$IFEND}
+
+{$IF (DEFINED(FPC) OR (CompilerVersion > 27.0))}
+function THorseCoreRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+{$IFNDEF FPC}
+function THorseCoreRoute.Patch(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute;
+begin
+ THorseCore.Patch(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+{$IFEND}
+
+function THorseCoreRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): IHorseCoreRoute;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+function THorseCoreRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): IHorseCoreRoute;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+// Corrigido typo do delete request
+function THorseCoreRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): IHorseCoreRoute;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+
+{$IFNDEF FPC}
+// Corrigido typo do delete response
+function THorseCoreRoute.Delete(const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): IHorseCoreRoute;
+begin
+ THorseCore.Delete(FPath, AMiddlewares, ACallback);
+ Result := Self;
+end;
+{$IFEND}
+{$IFEND}
+
end.
diff --git a/src/Horse.Core.pas b/src/Horse.Core.pas
index dbceede1..dbb9eafe 100644
--- a/src/Horse.Core.pas
+++ b/src/Horse.Core.pas
@@ -47,6 +47,7 @@ THorseCore = class(THorseCoreBase)
class var FCallbacks: TList;
class function TrimPath(const APath: string): string;
class function RegisterRoute(const AHTTPType: TMethodType; const APath: string; const ACallback: THorseCallback): THorseCore;
+ class function RegisterRouteMiddleware(const AHTTPType: TMethodType; const APath: string; const ACallback: THorseCallback): THorseCore;
class var FDefaultHorse: THorseCore;
function InternalRoute(const APath: string): IHorseCoreRoute;
@@ -139,6 +140,67 @@ THorseCore = class(THorseCoreBase)
{$IFNDEF FPC}
class function Query(const APath: string; const ACallback: THorseCallbackResponse): THorseCore; overload;
{$IFEND}
+
+ class function All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+
+ class function Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+
+ class function Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+
+{$IF (DEFINED(FPC) or (CompilerVersion > 27.0))}
+ class function Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+{$IFEND}
+
+ class function Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+
+ class function Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+
+{$IF (defined(fpc) or (CompilerVersion > 27.0))}
+ class function Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+{$IFEND}
+
+ class function Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore; overload;
+ class function Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore; overload;
+ class function Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore; overload;
+{$IFNDEF FPC}
+ class function Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore; overload;
+{$IFEND}
+
class property Routes: IHorseRouter read GetRoutes write SetRoutes;
class function GetInstance: THorseCore;
class function Version: string;
@@ -339,6 +401,15 @@ class function THorseCore.RegisterRoute(const AHTTPType: TMethodType; const APat
LDefaultHorse.GetRoutes.RegisterRoute(AHTTPType, TrimPath(APath), ACallback);
end;
+class function THorseCore.RegisterRouteMiddleware(const AHTTPType: TMethodType; const APath: string; const ACallback: THorseCallback): THorseCore;
+var
+ LDefaultHorse: THorseCore;
+begin
+ LDefaultHorse := GetInstance;
+ Result := LDefaultHorse;
+ LDefaultHorse.GetRoutes.RegisterRouteMiddleware(AHTTPType, TrimPath(APath), ACallback);
+end;
+
class function THorseCore.Route(const APath: string): IHorseCoreRoute;
begin
Result := GetInstance.InternalRoute(APath);
@@ -684,6 +755,226 @@ class function THorseCore.Put(const APath: string; const ACallback: THorseCallba
end;
{$IFEND}
+class function THorseCore.All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtAny, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtAny, APath, LMiddleware);
+ RegisterRoute(mtAny, APath, ACallback);
+end;
+
+class function THorseCore.All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := All(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := All(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.All(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := All(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+
+class function THorseCore.Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtGet, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtGet, APath, LMiddleware);
+ RegisterRoute(mtGet, APath, ACallback);
+end;
+
+class function THorseCore.Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := Get(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := Get(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.Get(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := Get(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+
+class function THorseCore.Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtPut, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtPut, APath, LMiddleware);
+ RegisterRoute(mtPut, APath, ACallback);
+end;
+
+class function THorseCore.Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := Put(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := Put(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.Put(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := Put(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+
+{$IF (DEFINED(FPC) or (CompilerVersion > 27.0))}
+class function THorseCore.Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtPatch, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtPatch, APath, LMiddleware);
+ RegisterRoute(mtPatch, APath, ACallback);
+end;
+
+class function THorseCore.Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := Patch(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := Patch(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.Patch(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := Patch(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+{$IFEND}
+
+class function THorseCore.Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtHead, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtHead, APath, LMiddleware);
+ RegisterRoute(mtHead, APath, ACallback);
+end;
+
+class function THorseCore.Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := Head(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := Head(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.Head(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := Head(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+
+class function THorseCore.Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtPost, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtPost, APath, LMiddleware);
+ RegisterRoute(mtPost, APath, ACallback);
+end;
+
+class function THorseCore.Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := Post(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := Post(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.Post(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := Post(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+
+{$IF (defined(fpc) or (CompilerVersion > 27.0))}
+class function THorseCore.Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtDelete, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtDelete, APath, LMiddleware);
+ RegisterRoute(mtDelete, APath, ACallback);
+end;
+
+class function THorseCore.Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := Delete(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := Delete(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.Delete(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := Delete(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+{$IFEND}
+
+class function THorseCore.Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallback): THorseCore;
+var
+ LMiddleware: THorseCallback;
+begin
+ Result := RegisterCallbacksRoute(mtQuery, APath);
+ for LMiddleware in AMiddlewares do
+ RegisterRouteMiddleware(mtQuery, APath, LMiddleware);
+ RegisterRoute(mtQuery, APath, ACallback);
+end;
+
+class function THorseCore.Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequestResponse): THorseCore;
+begin
+ Result := Query(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+class function THorseCore.Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackRequest): THorseCore;
+begin
+ Result := Query(APath, AMiddlewares, GetCallback(ACallback));
+end;
+
+{$IFNDEF FPC}
+class function THorseCore.Query(const APath: string; const AMiddlewares: array of THorseCallback; const ACallback: THorseCallbackResponse): THorseCore;
+begin
+ Result := Query(APath, AMiddlewares, GetCallback(ACallback));
+end;
+{$IFEND}
+
constructor THorseModule.Create(const ASelfInstance, ADefaultHorseCoreInstance: PHorseCore; const AHorseRouter: PHorseRouter);
begin
FSelfInstance := ASelfInstance;
diff --git a/tests/src/controllers/Controllers.Api.pas b/tests/src/controllers/Controllers.Api.pas
index a9f71c63..0254442b 100644
--- a/tests/src/controllers/Controllers.Api.pas
+++ b/tests/src/controllers/Controllers.Api.pas
@@ -42,12 +42,39 @@ procedure MiddlewareAuth(Req: THorseRequest; Res: THorseResponse; Next: TProc);
Next;
end;
+procedure MiddlewareRouteStep1(Req: THorseRequest; Res: THorseResponse; Next: TProc);
+begin
+ Res.AddHeader('X-Route-Step1', 'true');
+ Next;
+end;
+
+procedure MiddlewareRouteStep2(Req: THorseRequest; Res: THorseResponse; Next: TProc);
+begin
+ Res.AddHeader('X-Route-Step2', 'true');
+ Next;
+end;
+
procedure Registry;
var
LAuthMiddleware: THorseCallback;
begin
THorse.Use(MiddlewareCORS);
+ // Rota fluente com array de middlewares
+ THorse.Route('/Api/RouteMiddlewares')
+ .Get([MiddlewareRouteStep1, MiddlewareRouteStep2],
+ procedure(Req: THorseRequest; Res: THorseResponse)
+ begin
+ Res.Send('RouteMiddlewaresData');
+ end);
+
+ // Rota estática com array de middlewares
+ THorse.Get('/Api/StaticRouteMiddlewares', [MiddlewareRouteStep1, MiddlewareRouteStep2],
+ procedure(Req: THorseRequest; Res: THorseResponse)
+ begin
+ Res.Send('StaticRouteMiddlewaresData');
+ end);
+
LAuthMiddleware := MiddlewareAuth;
THorse.Route('/Api/Protected')
diff --git a/tests/src/tests/Tests.Api.Console.pas b/tests/src/tests/Tests.Api.Console.pas
index bc5d5489..19578399 100644
--- a/tests/src/tests/Tests.Api.Console.pas
+++ b/tests/src/tests/Tests.Api.Console.pas
@@ -53,6 +53,10 @@ TApiTest = class(TObject)
procedure TestMiddlewareCorsPreflightOptions;
[Test]
procedure TestCustomHeader;
+ [Test]
+ procedure TestFluentRouteMiddlewares;
+ [Test]
+ procedure TestStaticRouteMiddlewares;
end;
@@ -237,6 +241,40 @@ procedure TApiTest.TestCustomHeader;
end;
end;
+procedure TApiTest.TestFluentRouteMiddlewares;
+var
+ LClient: THTTPClient;
+ LResponse: IHTTPResponse;
+begin
+ LClient := THTTPClient.Create;
+ try
+ LResponse := LClient.Get('http://localhost:9000/Api/RouteMiddlewares');
+ Assert.AreEqual(200, LResponse.StatusCode);
+ Assert.AreEqual('RouteMiddlewaresData', LResponse.ContentAsString);
+ Assert.AreEqual('true', LResponse.HeaderValue['X-Route-Step1']);
+ Assert.AreEqual('true', LResponse.HeaderValue['X-Route-Step2']);
+ finally
+ LClient.Free;
+ end;
+end;
+
+procedure TApiTest.TestStaticRouteMiddlewares;
+var
+ LClient: THTTPClient;
+ LResponse: IHTTPResponse;
+begin
+ LClient := THTTPClient.Create;
+ try
+ LResponse := LClient.Get('http://localhost:9000/Api/StaticRouteMiddlewares');
+ Assert.AreEqual(200, LResponse.StatusCode);
+ Assert.AreEqual('StaticRouteMiddlewaresData', LResponse.ContentAsString);
+ Assert.AreEqual('true', LResponse.HeaderValue['X-Route-Step1']);
+ Assert.AreEqual('true', LResponse.HeaderValue['X-Route-Step2']);
+ finally
+ LClient.Free;
+ end;
+end;
+
procedure TApiTest.Setup;
begin
Writeln('TApiTest.Setup INICIADO!');