Skip to content
mobius1qwe edited this page Nov 5, 2026 · 1 revision

Home > Objetos no corpo > Controllers por atributos 🚧

Controllers por atributos e cliente por interface

Important

Em desenvolvimento (2.0). Ainda não está numa versão lançada e pode mudar.

Vale para a 2.0 · Delphi 2010 em diante (atributos e RTTI estendida; o cliente por interface pede XE2+) · Nível: intermediário

Em uma frase: escreva uma classe com métodos marcados ([GET('/{id}')], [POST]) e o TRALControllerModule cria as rotas; descreva a mesma API numa interface e o TRALRestProxy vira o cliente. Para quem vem do DMVC, MARS ou WiRL.

Você vai precisar: os pacotes RALModuleObjectsPackage e RALModuleControllersPackage. No Lazarus/FPC 3.2 o pacote compila vazio: use as rotas de objeto, que fazem o mesmo de forma RAD.

O controller

uses RALControllers, RALObjectRTTI;

type
  [Path('/produtos')]
  TProdutoController = class(TRALObject)
  public
    [GET]
    function Listar([FromQuery('nome')] AFiltro: string): TObjectList<TProduto>;
    [GET('/{id}')]
    function Obter(id: Integer): TProduto;
    [POST, Roles('admin')]
    function Criar(AProduto: TProduto; AResponse: TRALResponse): TProduto;
    [DELETE('/{id}')]
    procedure Apagar(id: Integer);
    [GET('/soma'), Produces('text/plain')]
    function Soma(a, b: Integer): string;
  end;

Modulo := TRALControllerModule.Create(Self);
Modulo.RegisterController(TProdutoController);
Modulo.Server := Servidor;

Ou, no Object Inspector, Controllers do módulo com o nome da classe registrada por RALRegisterController(TProdutoController).

De onde vem cada parâmetro

parâmetro vem de
[FromPath] ou um nome que está no caminho ({id}) o caminho
[FromQuery('nome')] ou um tipo simples sem atributo a query string
[FromHeader('X-Tenant')] o cabeçalho
[FromBody] ou uma classe, record ou array o corpo (validado: 422)
TRALRequest, TRALResponse o pedido e a resposta

Um texto que não converte (/produtos/abc para um Integer) é 400.

O que o método devolve

Um objeto sai no formato do Accept (e é liberado); record, array, número, booleano em JSON; string com [Produces('text/plain')] sai como texto; procedure responde 204. O status padrão é 200; mude em AResponse.StatusCode. Uma exceção segue o ExceptionMap do servidor (por exemplo, ENaoEncontrado → 404).

O controller é criado a cada pedido e liberado depois. [Roles('admin')] vira o Roles da rota (papéis e políticas); [AllowAnonymous] dispensa autenticação. Entre /produtos/soma e /produtos/{id}, o caminho literal é registrado primeiro e ganha.

O cliente por interface

uses RALControllers, RALRestProxy;

type
  IProdutos = interface(IInvokable)
    ['{6B0F1E1A-7C38-4C55-9C3C-2B1E5A0C9D11}']
    [GET('/produtos/{id}')]
    function Obter(id: Integer): TProduto;
    [POST('/produtos')]
    function Criar(AProduto: TProduto): TProduto;
    [GET('/produtos')]
    function Listar([FromQuery('nome')] AFiltro: string): TArray<TProduto>;
  end;

Api := TRALRestProxy.Make<IProdutos>(Cliente);   // Cliente: um TRALClient
Produto := Api.Obter(1);                         // você libera o objeto

Uma resposta 400 ou maior (ou nenhuma) levanta ERALRestProxy com Status e Body.

Erros comuns

sintoma causa
nenhuma rota criada método sem atributo de verbo, ou declarado na seção private (precisa de RTTI: public/published)
"has no GUID" a interface precisa de ['{...}'] e herdar de IInvokable
parâmetro sempre vazio o nome do parâmetro não é o da query: use [FromQuery('nome')]

Veja também

Clone this wiki locally