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

· Lançamentos

A próxima versão do sgcOpenAPI — versão 2026.6, prevista para junho — traz um componente novo em folha: TsgcHTTPServer_OpenAPI. É um componente Delphi único e autocontido que hospeda um serviço OpenAPI 3.0 com um servidor HTTP embutido. Coloque-o em um formulário, aponte para uma especificação (ou gere uma a partir de uma classe Delphi com atributos RTTI), defina Active := True, 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. O novo componente é fornecido, empacotado e instalado inteiramente a partir do sgcOpenAPI. Se você já usa o sgcWebSockets, o componente anterior TsgcWSServer_API_OpenAPI continua funcionando sem alterações — ambos compartilham o mesmo motor internamente.

O que você obtém em um único componente

O TsgcHTTPServer_OpenAPI reúne três elementos:

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

Isto é tudo o que você precisa para hospedar um servidor OpenAPI funcional com Swagger UI:

uses
  sgcHTTPServer_OpenAPI;

var
  oServer: TsgcHTTPServer_OpenAPI;
begin
  oServer := TsgcHTTPServer_OpenAPI.Create(nil);
  try
    oServer.Bindings.Add.Port := 8080;
    oServer.LoadFromFile('petstore.json');
    oServer.OnRequest := MyOnRequest;
    oServer.Active := True;
    Readln;
  finally
    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 ou YAML (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 operationId de cada rota é a chave de despacho. Dentro de OnRequest você trata cada operação por vez:

uses
  sgcHTTP_OpenAPI_Server, sgcHTTP_OpenAPI_Server_Engine,
  sgcHTTPServer_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_OpenAPI_Server_CodeFirst, sgcHTTPServer_OpenAPI;

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

  oServer := TsgcHTTPServer_OpenAPI.Create(nil);
  oServer.LoadFromString(vSpec);
  oServer.Bindings.Add.Port := 8081;
  oServer.OnRequest := MyOnRequest;
  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, agrupada em três subopções:

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

O componente expõe seis eventos para 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.

Demos

Duas demos completas acompanham o sgcOpenAPI 2026.6, ambas usando apenas o novo componente independente — sem necessidade de instalação do sgcWebSockets:

Atualização

Se você atualmente usa TsgcWSServer_API_OpenAPI com sgcWebSockets, nada muda — a classe, as propriedades e os eventos são todos preservados, e a implementação agora delega ao mesmo motor compartilhado que alimenta o novo componente independente. Você pode manter seu código existente como está, ou migrar um formulário por vez para TsgcHTTPServer_OpenAPI para eliminar a dependência do sgcWebSockets.

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.