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!');