Skip to content
mobius1qwe edited this page Oct 7, 2026 · 1 revision

Criando um módulo

Home > Criando um módulo

Important

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

Nível: avançado. Até a 1.2, um módulo só consegue criar rotas com o TRALRequest/TRALResponse de sempre.

Um módulo é um componente que se liga a um TRALServer (propriedade Server) e traz as rotas dele. O TRALDBModule, o TRALWebModule e o TRALSwaggerModule são módulos. Todo módulo descende de TRALModuleRoutes (unit RALServer), e um módulo de terceiros usa exatamente os mesmos pontos de extensão que eles.

Do mais simples ao mais completo, um módulo pode ter:

o quê como
rotas CreateRoute no construtor, com o handler de sempre
algo antes e depois de toda rota dele sobrescreve BeforeExecute/AfterExecute
dados próprios em cada rota, ou um handler de outro tipo a própria classe de rota: RouteClass
pedido e resposta próprios, com métodos que só ele tem RequestClass/ResponseClass + ExecuteContext
uma resposta de erro própria HandleException
reagir ao servidor ligando e desligando ServerActivating/ServerDeactivating

Pedido e resposta do módulo

O handler de uma rota comum recebe o TRALRequest e o TRALResponse do núcleo. Um módulo pode entregar aos handlers dele outras classes, com métodos que o resto do projeto não tem. O TRALDBModule, por exemplo, tem um AResponse.Answer(SQLCache) que só as rotas dele enxergam.

Essas classes não são cópias do pedido nem da resposta. TRALModuleRequest e TRALModuleResponse guardam o objeto do núcleo (Core) e escrevem nele, e é esse objeto que o engine envia. O módulo cria as duas no início da rota e as libera no fim.

uses
  Classes, SysUtils,
  RALServer, RALRoutes, RALRequest, RALResponse, RALTypes, RALConsts,
  RALMIMETypes;

type
  // a resposta do módulo: um Answer que só as rotas dele têm
  TSaudacaoResponse = class(TRALModuleResponse)
  public
    procedure Answer(const ANome: StringRAL; AVezes: Integer); overload;
  end;

  TSaudacaoOnReply = procedure(ARequest: TRALModuleRequest;
    AResponse: TSaudacaoResponse) of object;

  // a rota do módulo: um dado por rota e o handler tipado
  TSaudacaoRoute = class(TRALRoute)
  private
    FVezes: Integer;
    FOnSaudacao: TSaudacaoOnReply;
  public
    property Vezes: Integer read FVezes write FVezes;
    property OnSaudacao: TSaudacaoOnReply read FOnSaudacao write FOnSaudacao;
  end;

  TSaudacaoModule = class(TRALModuleRoutes)
  private
    procedure Ola(ARequest: TRALModuleRequest; AResponse: TSaudacaoResponse);
  protected
    class function RouteClass: TRALRouteClass; override;
    class function ResponseClass: TRALModuleResponseClass; override;
    procedure ExecuteContext(ARoute: TRALRoute; ARequest: TRALModuleRequest;
      AResponse: TRALModuleResponse); override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

implementation

procedure TSaudacaoResponse.Answer(const ANome: StringRAL; AVezes: Integer);
var
  vTexto: StringRAL;
  vInt: Integer;
begin
  vTexto := '';
  for vInt := 1 to AVezes do
    vTexto := vTexto + 'ola ' + ANome + ' ';
  Answer(HTTP_OK, Trim(vTexto), rctTEXTPLAIN); // a sobrecarga herdada
end;

class function TSaudacaoModule.RouteClass: TRALRouteClass;
begin
  Result := TSaudacaoRoute;
end;

class function TSaudacaoModule.ResponseClass: TRALModuleResponseClass;
begin
  Result := TSaudacaoResponse;
end;

constructor TSaudacaoModule.Create(AOwner: TComponent);
var
  vRota: TSaudacaoRoute;
begin
  inherited Create(AOwner);
  // NewRoute cria a rota da classe do módulo, com caminho e descrição
  vRota := TSaudacaoRoute(NewRoute('ola/:nome', 'Cumprimenta'));
  vRota.Vezes := 2;
  vRota.OnSaudacao := {$IFDEF FPC}@{$ENDIF}Ola;
  vRota.AllowedMethods := [amGET];
end;

procedure TSaudacaoModule.ExecuteContext(ARoute: TRALRoute;
  ARequest: TRALModuleRequest; AResponse: TRALModuleResponse);
begin
  if (ARoute is TSaudacaoRoute) and Assigned(TSaudacaoRoute(ARoute).OnSaudacao) then
    TSaudacaoRoute(ARoute).OnSaudacao(ARequest, TSaudacaoResponse(AResponse))
  else
    inherited; // sem handler: 404
end;

procedure TSaudacaoModule.Ola(ARequest: TRALModuleRequest;
  AResponse: TSaudacaoResponse);
begin
  AResponse.Answer(ARequest.ParamByName('nome').AsString,
    TSaudacaoRoute(ARequest.Route).Vezes);
end;

Com Domain := '/saudacao', um GET /saudacao/ola/ana responde ola ana ola ana.

O que saber antes de escrever o seu

  • overload no Answer novo. Com a diretiva, o Answer da classe do módulo se soma às sobrecargas herdadas (status + texto, status + stream, status, arquivo). Sem ela, esconde todas. Vale no Delphi e no FPC.
  • Rota com handler do núcleo continua como sempre. Se a rota tem OnReply (o CreateRoute de sempre), ela recebe o TRALRequest/TRALResponse do núcleo e o módulo não cria o contexto. O contexto só existe para as rotas sem esse handler. Por isso um módulo pode misturar os dois tipos de rota.
  • O que a classe não repete está em Core. ARequest.Core é o TRALRequest, e AResponse.Core é o TRALResponse.
  • Estado de um pedido fica na classe do pedido. O TRALDBRequest pega a conexão do pool no primeiro uso e a devolve no destrutor, e nenhuma rota do banco escreve try/finally para isso.
  • OPTIONS, 405 e autenticação são do núcleo. O módulo não precisa tratar nenhum dos três: o método fora de AllowedMethods responde 405 antes de o handler rodar.

Erro próprio

function TMeuModule.HandleException(ARequest: TRALRequest;
  AResponse: TRALResponse; AException: Exception): boolean;
begin
  Result := True; // respondi: o servidor não responde 500 nem chama OnServerError
  AResponse.Answer(422, '{"erro":"' + AException.Message + '"}', rctAPPLICATIONJSON);
end;

HandleException recebe as exceções de BeforeExecute, da rota e de AfterExecute daquele módulo. Devolvendo False (o padrão), a exceção segue para o servidor, que responde 500 e dispara OnServerError, como sempre.

Servidor ligando e desligando

  • ServerActivating roda quando o servidor recebe Active := True, ou quando o módulo é ligado a um servidor que já está ativo.
    • Roda antes de o engine abrir a porta.
    • Uma exceção ali deixa o servidor parado, e os módulos que já tinham ouvido o start ouvem o stop.
    • O TRALDBModule usa esse momento para abrir o pool (PoolOptions.PrepareOnActivate).
  • ServerDeactivating roda quando o servidor recebe Active := False, quando o módulo sai de um servidor ativo, ou quando o servidor é liberado ativo.
    • Pedidos podem ainda estar rodando nesse momento: não libere o que uma rota pode estar usando.
    • Uma exceção ali vai para OnServerError e nunca impede o servidor de parar.
  • Nenhum dos dois roda em design time. Ao carregar um form, rodam no Loaded do módulo, depois que as propriedades dele foram lidas.

Na paleta

procedure Register;
begin
  RegisterComponents('RAL - Modules', [TSaudacaoModule]);
end;

Não publique Routes num módulo que cria as rotas no construtor. O .dfm passaria a gravar essas rotas, e ao recarregar o form a coleção seria refeita sem os handlers. O TRALWebModule publica Routes porque as rotas dele são editadas pelo usuário.

Clone this wiki locally