Repository navigation
criar modulo
Home > Criando um módulo
Important
Em desenvolvimento (2.0). 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 |
outro Content-Type padrão para as respostas dele |
DefaultContentType |
| 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.
DefaultContentType diz em que tipo saem as respostas do módulo quando a rota
não diz outro: as do Answer sem tipo e as que o próprio módulo dá (405,
OPTIONS). Vazio, o padrão do núcleo, é JSON. O servidor SOAP
responde application/xml:
function TMeuModulo.DefaultContentType: StringRAL;
begin
Result := rctAPPLICATIONXML;
end;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
- Servidor SOAP 🚧
- 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