sgcOpenAPI 2026.6 — Servidor OpenAPI independiente, Spec-First o Code-First

· Versiones

La próxima versión de sgcOpenAPI — la 2026.6, prevista para junio — incorpora un componente totalmente nuevo: TsgcHTTPServer_OpenAPI. Se trata de un único componente Delphi autónomo que aloja un servicio OpenAPI 3.0 con un servidor HTTP integrado. Colócalo en un formulario, apúntalo a una especificación (o genérala desde una clase Delphi con atributos RTTI), establece Active := True y tendrás una API REST documentada con Swagger UI servido automáticamente.

El cambio principal es que sgcOpenAPI ya no necesita sgcWebSockets para alojar un servidor HTTP. El nuevo componente se distribuye, empaqueta e instala íntegramente desde sgcOpenAPI. Si ya usas sgcWebSockets, el componente anterior TsgcWSServer_API_OpenAPI sigue funcionando sin cambios — ambos comparten el mismo motor internamente.

Lo que obtienes en un solo componente

TsgcHTTPServer_OpenAPI agrupa tres elementos:

Inicio rápido — el ejemplo mínimo

Esto es todo lo que necesitas para alojar un servidor OpenAPI funcional con 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;

Accede a http://localhost:8080/docs para ver el Swagger UI y a http://localhost:8080/openapi.json para la especificación. Cada operación definida en la especificación se enruta a tu manejador MyOnRequest con el operationId resuelto y un contexto de solicitud completamente construido.

Spec-First — carga un archivo OpenAPI 3.0 existente

Si ya dispones de un archivo OpenAPI 3.0 en JSON o YAML (Petstore, un contrato de API interno, un esquema público que quieras simular), spec-first es la forma más rápida de servirlo. LoadFromFile lee y analiza la especificación, construye una tabla de rutas a partir de la sección paths y compara cada solicitud entrante con ella.

El operationId de cada ruta es la clave de despacho. Dentro de OnRequest gestionas cada operación por turno:

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;

El TsgcOpenAPIServerContext te proporciona acceso tipado a todo lo que contiene la solicitud: PathParamAsString / PathParamAsInteger para los segmentos con plantilla, QueryParamAsString / QueryParamAsInteger / QueryParamAsBoolean con valores por defecto, BodyAsString / BodyAsJSON para el cuerpo de la solicitud y HeaderValue para cualquier cabecera entrante. Para responder, utiliza los ayudantes RespondJSON(code, content) y RespondError(code, title, detail), o establece directamente Response.Code, Response.ContentType y Response.Content para un control total.

Code-First — genera la especificación desde una clase Delphi

Si prefieres escribir el contrato de la API en Delphi y dejar que la especificación se genere, decora una clase con atributos RTTI. TsgcOpenAPICodeFirstScanner recorre la clase, construye un documento JSON OpenAPI 3.0 completo y tú lo cargas en el servidor con LoadFromString. Esto requiere Delphi XE7 o posterior (para RTTI extendido).

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;

Los cuerpos de los métodos son stubs — existen únicamente para que el compilador emita información RTTI para ellos. El trabajo real ocurre en OnRequest, despachado por el operationId que el escáner deriva del nombre de cada método (ListTasks, CreateTask, GetTask…).

Entrega la clase al escáner al inicio y carga la especificación generada en el 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;

Los atributos cubren los metadatos habituales: sgcServiceContract rellena el bloque info de OpenAPI, sgcRoute establece la ruta a nivel de clase o de método, sgcHttpGet / Post / Put / Delete / Patch / Head / Options elige el verbo, sgcSummary y sgcDescription documentan la operación, sgcTag la agrupa en Swagger UI, sgcResponse(code, description) declara cada respuesta y sgcFromPath / FromQuery / FromBody / FromHeader junto con sgcRequired describen cada parámetro.

Configuración — OpenAPIOptions

Toda la configuración del lado del servidor se encuentra bajo OpenAPIOptions, agrupada en tres subopciones:

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;

Con la validación activada, cada solicitud entrante se comprueba frente a los JSON Schemas declarados en la especificación antes de llegar a tu manejador — campos obligatorios, tipos, formatos, enumeraciones, rangos. Los fallos disparan el evento OnValidationError con la lista de errores y una bandera para aceptar o rechazar la solicitud.

Eventos

El componente expone seis eventos para el ciclo de vida de la solicitud:

OnBeforeRequest: se dispara antes del despacho; establece Accept := False para rechazar con un 403 Forbidden. Útil para limitación de tasa, registro o controles por ruta.

OnAuthenticate: se dispara antes del manejador principal; establece Authenticated := False para rechazar con 401 Unauthorized. Inspecciona cabeceras, cookies o parámetros de consulta para decidir.

OnValidationError: se dispara cuando la validación falla; recibe la lista de errores. Establece Continue := False para rechazar con 400 Bad Request.

OnRequest: el evento principal de despacho. Examina aOperationId, escribe la respuesta en aContext.Response y establece Handled := True.

OnAfterRequest: se dispara después de que el manejador retorne — ideal para métricas o registros de auditoría.

OnException: se dispara si una excepción no controlada se propaga fuera de tu manejador. Ajusta aResponseCode si quieres algo distinto de 500 Internal Server Error.

Demos

Con sgcOpenAPI 2026.6 se incluyen dos demos completos, ambos usando únicamente el nuevo componente independiente — sin necesidad de instalar sgcWebSockets:

Actualización

Si actualmente usas TsgcWSServer_API_OpenAPI con sgcWebSockets, nada cambia — la clase, las propiedades y los eventos se mantienen y la implementación ahora delega en el mismo motor compartido que impulsa el nuevo componente independiente. Puedes mantener tu código existente tal cual, o migrar un formulario cada vez a TsgcHTTPServer_OpenAPI para eliminar la dependencia de sgcWebSockets.

sgcOpenAPI 2026.6 estará disponible en la página de descargas en junio.

¿Preguntas, comentarios o ayuda con la migración? Ponte en contacto — recibirás respuesta de las personas que escribieron el código.