Repository navigation
criar modulo
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/TRALResponsede 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
|
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.
-
overloadnoAnswernovo. Com a diretiva, oAnswerda 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(oCreateRoutede sempre), ela recebe oTRALRequest/TRALResponsedo 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é oTRALRequest, eAResponse.Coreé oTRALResponse. -
Estado de um pedido fica na classe do pedido. O
TRALDBRequestpega a conexão do pool no primeiro uso e a devolve no destrutor, e nenhuma rota do banco escrevetry/finallypara 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
AllowedMethodsresponde 405 antes de o handler rodar.
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.
-
ServerActivatingroda quando o servidor recebeActive := 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
TRALDBModuleusa esse momento para abrir o pool (PoolOptions.PrepareOnActivate).
-
ServerDeactivatingroda quando o servidor recebeActive := 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
OnServerErrore nunca impede o servidor de parar.
- Nenhum dos dois roda em design time. Ao carregar um form, rodam no
Loadeddo módulo, depois que as propriedades dele foram lidas.
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.
Começar
Servidor
- Rotas e parâmetros
- Ler o pedido e responder
- Plugins 🚧
- Autenticação
- Banco de dados (DBWare)
- Servir site e arquivos
- Swagger e Postman
- HTTPS
- Criptografia ponto a ponto
- Segurança para produção
- Colocar no ar
- Desempenho
Cliente
Motores
Avançado
Ajuda
- Problemas comuns
- Atualizar o Indy
- Várias versões do Delphi
- Vídeos e guias
- Módulos de terceiros
- Licenças
- Contribuir
- Roadmap e changelog
🚧 = em desenvolvimento, pode mudar