sgcOpenAPI 2026.6 — Servidor OpenAPI Independente, Spec-First ou Code-First

· Lançamentos
sgcOpenAPI 2026.6 — Servidor OpenAPI Independente, Spec-First ou Code-First | Blog eSeGeCe

A próxima versão do sgcOpenAPI, versão 2026.6, prevista para junho, traz um servidor OpenAPI 3.0 que você pode hospedar sem instalar o sgcWebSockets. Dois componentes fazem isso: TsgcHTTPServer, o servidor HTTP baseado em Indy, e TsgcWSAPIServer_OpenAPI, o componente de API que você anexa a ele. Aponte o componente de API para uma especificação (ou gere uma a partir de uma classe Delphi com atributos RTTI), atribua a sua propriedade Server, inicie o servidor HTTP, e você tem uma API REST documentada com Swagger UI servido automaticamente.

A mudança principal é que o sgcOpenAPI não precisa mais do sgcWebSockets para hospedar um servidor HTTP. Os dois componentes são fornecidos, empacotados e instalados inteiramente a partir do sgcOpenAPI. Se você já usa o sgcWebSockets, o componente TsgcWSServer_API_OpenAPI que você já conhece continua funcionando sem alterações, porque é a mesma classe: os dois produtos o constroem a partir do mesmo motor.

O que você obtém

Os dois componentes trazem três coisas:

Início rápido — o exemplo mínimo

Isto é tudo o que você precisa para hospedar um servidor OpenAPI funcional com Swagger UI. Note que o componente de API não tem propriedade Active própria: atribuir Server é o que o anexa, e atribuir nil o desanexa enquanto o servidor HTTP continua em execução.

uses
  sgcHTTP_Server, sgcWebSocket_Server_API_OpenAPI;

var
  oServer: TsgcHTTPServer;
  oOpenAPI: TsgcWSAPIServer_OpenAPI;
begin
  oServer := TsgcHTTPServer.Create(nil);
  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  try
    oServer.Bindings.Add.Port := 8080;
    oOpenAPI.LoadFromFile('petstore.json');
    oOpenAPI.OnRequest := MyOnRequest;
    oOpenAPI.Server := oServer;
    oServer.Active := True;
    Readln;
  finally
    oOpenAPI.Free;
    oServer.Free;
  end;
end;

Acesse http://localhost:8080/docs para o Swagger UI e http://localhost:8080/openapi.json para a especificação. Toda operação definida na especificação é roteada para o seu handler MyOnRequest com o operationId resolvido e um contexto de requisição totalmente construído.

Spec-First — carregue um arquivo OpenAPI 3.0 existente

Se você já tem um arquivo OpenAPI 3.0 em JSON (Petstore, um contrato de API interno, um esquema público que deseja simular), o spec-first é a maneira mais rápida de servi-lo. LoadFromFile lê e analisa a especificação, constrói uma tabela de rotas a partir da seção paths e compara cada requisição recebida com ela. O servidor lê JSON, então converta um contrato YAML antes, por exemplo com o sgcOpenAPI.exe.

O operationId de cada rota é a chave de despacho. Dentro de OnRequest você trata cada operação por vez:

uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server,
  sgcHTTP_OpenAPI_Server_Engine, sgcWebSocket_Server_API_OpenAPI;

procedure TForm1.OnOpenAPIRequest(Sender: TObject;
  const aOperationId: string; const aContext: TsgcOpenAPIServerContext;
  var Handled: Boolean);
begin
  Handled := True;
  if aOperationId = 'listPets' then
    HandleListPets(aContext)
  else if aOperationId = 'getPetById' then
    HandleGetPetById(aContext)
  else if aOperationId = 'createPet' then
    HandleCreatePet(aContext)
  else
    Handled := False;
end;

procedure TForm1.HandleGetPetById(const aContext: TsgcOpenAPIServerContext);
var
  vId, vPetJSON: string;
begin
  vId := aContext.PathParamAsString('petId');
  vPetJSON := FPets.Values[vId];
  if vPetJSON <> '' then
    aContext.RespondJSON(200, vPetJSON)
  else
    aContext.RespondError(404, 'Not Found', 'Pet ' + vId + ' not found');
end;

O TsgcOpenAPIServerContext fornece acessadores tipados para tudo o que está na requisição: PathParamAsString / PathParamAsInteger para segmentos baseados em modelo, QueryParamAsString / QueryParamAsInteger / QueryParamAsBoolean com valores padrão, BodyAsString / BodyAsJSON para o corpo da requisição, e HeaderValue para qualquer cabeçalho recebido. Para responder, use os helpers RespondJSON(code, content) e RespondError(code, title, detail), ou defina Response.Code, Response.ContentType e Response.Content diretamente para controle total.

Code-First — gere a especificação a partir de uma classe Delphi

Se você preferir escrever o contrato da API em Delphi e deixar a especificação ser gerada, decore uma classe com atributos RTTI. O TsgcOpenAPICodeFirstScanner percorre a classe, constrói um documento JSON OpenAPI 3.0 completo, e você carrega isso no servidor com LoadFromString. Isto requer Delphi XE7 ou mais recente (para RTTI estendido).

uses
  sgcHTTP_OpenAPI_Server_CodeFirst;

type
  [sgcServiceContract('Task Manager API',
    'A simple task management demo', '1.0.0')]
  [sgcRoute('/api/v1')]
  TTaskManagerService = class
  public
    [sgcHttpGet]
    [sgcRoute('/tasks')]
    [sgcSummary('List all tasks')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'A list of tasks')]
    procedure ListTasks([sgcFromQuery] const status: string); virtual;

    [sgcHttpPost]
    [sgcRoute('/tasks')]
    [sgcSummary('Create a new task')]
    [sgcTag('Tasks')]
    [sgcResponse(201, 'Task created successfully')]
    procedure CreateTask([sgcFromBody] const body: string); virtual;

    [sgcHttpGet]
    [sgcRoute('/tasks/{taskId}')]
    [sgcSummary('Get a task by ID')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'The requested task')]
    [sgcResponse(404, 'Task not found')]
    procedure GetTask([sgcFromPath][sgcRequired]
      const taskId: Integer); virtual;
  end;

Os corpos dos métodos são stubs — eles existem apenas para que o compilador emita RTTI para eles. O trabalho real acontece em OnRequest, despachado pelo operationId que o scanner deriva de cada nome de método (ListTasks, CreateTask, GetTask…).

Entregue a classe ao scanner na inicialização e carregue a especificação gerada no servidor:

uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server_CodeFirst,
  sgcWebSocket_Server_API_OpenAPI;

var
  oScanner: TsgcOpenAPICodeFirstScanner;
  oServer: TsgcHTTPServer;
  oOpenAPI: TsgcWSAPIServer_OpenAPI;
  vSpec: string;
begin
  oScanner := TsgcOpenAPICodeFirstScanner.Create;
  try
    vSpec := oScanner.GenerateSpec(TTaskManagerService);
  finally
    oScanner.Free;
  end;

  oServer := TsgcHTTPServer.Create(nil);
  oServer.Bindings.Add.Port := 8081;

  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  oOpenAPI.LoadFromString(vSpec);
  oOpenAPI.OnRequest := MyOnRequest;
  oOpenAPI.Server := oServer;

  oServer.Active := True;
end;

Os atributos cobrem os metadados comuns: sgcServiceContract preenche o bloco info do OpenAPI, sgcRoute define o caminho no nível da classe ou do método, sgcHttpGet / Post / Put / Delete / Patch / Head / Options escolhe o verbo, sgcSummary e sgcDescription documentam a operação, sgcTag a agrupa no Swagger UI, sgcResponse(code, description) declara cada resposta, e sgcFromPath / FromQuery / FromBody / FromHeader em conjunto com sgcRequired descrevem cada parâmetro.

Configuração — OpenAPIOptions

Toda a configuração do lado do servidor fica em OpenAPIOptions, no componente de API, agrupada em cinco subopções. Estas três reúnem as configurações do dia a dia:

oServer.OpenAPIOptions.Endpoint.BasePath        := '/api';
oServer.OpenAPIOptions.Endpoint.ServeSpec       := True;   // /openapi.json
oServer.OpenAPIOptions.Endpoint.ServeSwaggerUI  := True;   // /docs

oServer.OpenAPIOptions.CORS.Enabled             := True;
oServer.OpenAPIOptions.CORS.AllowOrigins        := '*';
oServer.OpenAPIOptions.CORS.AllowHeaders        := 'Content-Type, Authorization';
oServer.OpenAPIOptions.CORS.AllowMethods        := 'GET, POST, PUT, DELETE, PATCH, OPTIONS';

oServer.OpenAPIOptions.Validation.ValidateRequest     := True;
oServer.OpenAPIOptions.Validation.ValidateRequestBody := True;
oServer.OpenAPIOptions.Validation.ValidateQueryParams := True;
oServer.OpenAPIOptions.Validation.ValidatePathParams  := True;
oServer.OpenAPIOptions.Validation.ValidateRequired    := True;

Com a validação ativada, toda requisição recebida é checada em relação aos JSON Schemas declarados na especificação antes de chegar ao seu handler — campos obrigatórios, tipos, formatos, enums, intervalos. Falhas disparam o evento OnValidationError com a lista de erros e um flag para aceitar ou rejeitar a requisição.

Eventos

Seis eventos cobrem o ciclo de vida da requisição:

OnBeforeRequest: dispara antes do despacho; defina Accept := False para rejeitar com 403 Forbidden. Útil para limitação de taxa, registro de logs ou controles por rota.

OnAuthenticate: dispara antes do handler principal; defina Authenticated := False para rejeitar com 401 Unauthorized. Inspecione cabeçalhos, cookies ou parâmetros de consulta para decidir.

OnValidationError: dispara quando a validação falha; recebe a lista de erros. Defina Continue := False para rejeitar com 400 Bad Request.

OnRequest: o evento principal de despacho. Observe o aOperationId, escreva a resposta em aContext.Response, defina Handled := True.

OnAfterRequest: dispara depois que o handler retorna — ideal para métricas ou log de auditoria.

OnException: dispara se uma exceção não tratada surgir do seu handler. Ajuste aResponseCode se quiser algo diferente de 500 Internal Server Error.

As duas subopções restantes têm as suas próprias: Security conduz o OnValidateAPIKey, o OnValidateBasic e o OnValidateBearer para os securitySchemes que a especificação declara, e Mock responde a uma operação sem handler usando os próprios exemplos da especificação.

Demos

Duas demos completas acompanham o sgcOpenAPI 2026.6, ambas hospedadas pela dupla independente, portanto nenhuma instalação do sgcWebSockets é necessária:

Atualização

Se você atualmente usa TsgcWSServer_API_OpenAPI com sgcWebSockets, nada muda. A classe, as suas propriedades e os seus eventos são todos preservados, e a implementação delega ao motor compartilhado. O TsgcWSAPIServer_OpenAPI é o descendente publicado dessa mesma classe, então a única coisa que o sgcOpenAPI muda é de onde vem o pacote.

O sgcOpenAPI 2026.6 estará disponível na página de downloads em junho.

Dúvidas, feedback ou ajuda com a migração? Entre em contato — você receberá uma resposta das pessoas que escreveram o código.