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

· Versiones
sgcOpenAPI 2026.6 — Servidor OpenAPI independiente, Spec-First o Code-First | Blog de eSeGeCe

La próxima versión de sgcOpenAPI, la 2026.6, prevista para junio, incorpora un servidor OpenAPI 3.0 que puedes alojar sin instalar sgcWebSockets. Lo consiguen dos componentes: TsgcHTTPServer, el servidor HTTP basado en Indy, y TsgcWSAPIServer_OpenAPI, el componente de API que conectas a él. Apunta el componente de API a una especificación (o genérala desde una clase Delphi con atributos RTTI), asigna su propiedad Server, arranca el servidor HTTP, 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. Ambos componentes se distribuyen, empaquetan e instalan íntegramente desde sgcOpenAPI. Si ya usas sgcWebSockets, el componente TsgcWSServer_API_OpenAPI que ya conoces sigue funcionando sin cambios, porque es la misma clase: ambos productos lo construyen desde el mismo motor.

Lo que obtienes

La pareja aporta tres cosas:

Inicio rápido — el ejemplo mínimo

Esto es todo lo que necesitas para alojar un servidor OpenAPI funcional con Swagger UI. Ten en cuenta que el componente de API no tiene su propia propiedad Active: asignar Server es lo que lo conecta, y asignarle nil lo desconecta mientras el servidor HTTP sigue funcionando.

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;

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 (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 servidor lee JSON, así que convierte primero un contrato YAML, por ejemplo con sgcOpenAPI.exe.

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

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;

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_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;

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, en el componente de API, agrupada en cinco subopciones. Estas tres reúnen los ajustes habituales:

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

Seis eventos cubren 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.

Las dos subopciones restantes aportan lo suyo: Security impulsa OnValidateAPIKey, OnValidateBasic y OnValidateBearer para los securitySchemes que declara la especificación, y una respuesta sintética para una operación que todavía nadie ha implementado.

Demos

Con sgcOpenAPI 2026.6 se incluyen dos demos completos, ambos alojados por la pareja independiente, por lo que no se requiere instalar sgcWebSockets:

Actualización

Si actualmente usas TsgcWSServer_API_OpenAPI con sgcWebSockets, nada cambia. La clase, sus propiedades y sus eventos se conservan, y la implementación delega en el motor compartido. TsgcWSAPIServer_OpenAPI es el descendiente publicado de esa misma clase, así que lo único que cambia con sgcOpenAPI es de dónde viene el paquete.

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.