sgcOpenAPI 2026.6 — Standalone OpenAPI-server, Spec-First of Code-First

· Releases
sgcOpenAPI 2026.6 — Standalone OpenAPI-server, Spec-First of Code-First

De volgende release van sgcOpenAPI, versie 2026.6, verwacht in juni, levert een OpenAPI 3.0-server die je kunt hosten zonder sgcWebSockets te installeren. Twee componenten doen het werk: TsgcHTTPServer, de op Indy gebaseerde HTTP-server, en TsgcWSAPIServer_OpenAPI, de API-component die je eraan koppelt. Wijs de API-component naar een spec (of genereer er een uit een Delphi-klasse met RTTI-attributen), ken de eigenschap Server ervan toe, start de HTTP-server, en je hebt een gedocumenteerde REST API met automatisch geserveerde Swagger UI.

De belangrijkste wijziging is dat sgcOpenAPI geen sgcWebSockets meer nodig heeft om een HTTP-server te hosten. Beide componenten worden volledig vanuit sgcOpenAPI geleverd, verpakt en geïnstalleerd. Als je al sgcWebSockets gebruikt, blijft de vertrouwde TsgcWSServer_API_OpenAPI-component ongewijzigd werken, want het is dezelfde klasse: beide producten bouwen hem vanuit dezelfde engine.

Wat je krijgt

Samen leveren ze drie dingen:

Snelstart — het minimale voorbeeld

Dit is alles wat je nodig hebt om een werkende OpenAPI-server met Swagger UI te hosten. Let op dat de API-component geen eigen Active-eigenschap heeft: door Server toe te wijzen wordt hij gekoppeld, en door nil toe te wijzen wordt hij losgekoppeld terwijl de HTTP-server blijft draaien.

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;

Navigeer naar http://localhost:8080/docs voor de Swagger UI en http://localhost:8080/openapi.json voor de spec. Elke operatie die in de spec is gedefinieerd, wordt naar je MyOnRequest-handler gerouteerd met het opgeloste operationId en een volledig opgebouwde request-context.

Spec-First — laad een bestaand OpenAPI 3.0-bestand

Als je al een OpenAPI 3.0 JSON-bestand hebt (Petstore, een intern API-contract, een openbaar schema dat je wilt mocken), is spec-first de snelste manier om het te serveren. LoadFromFile leest en parseert de spec, bouwt een routetabel op uit de paths-sectie, en matcht elk binnenkomend verzoek daartegen. De server leest JSON, dus converteer een YAML-contract eerst, bijvoorbeeld met sgcOpenAPI.exe.

Het operationId van elke route is de dispatch-sleutel. Binnen OnRequest handel je elke operatie achtereenvolgens af:

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;

De TsgcOpenAPIServerContext geeft je getypeerde accessors voor alles in het verzoek: PathParamAsString / PathParamAsInteger voor template-segmenten, QueryParamAsString / QueryParamAsInteger / QueryParamAsBoolean met standaardwaarden, BodyAsString / BodyAsJSON voor de request-body, en HeaderValue voor elke binnenkomende header. Om te antwoorden gebruik je de helpers RespondJSON(code, content) en RespondError(code, title, detail), of stel je Response.Code, Response.ContentType en Response.Content rechtstreeks in voor volledige controle.

Code-First — genereer de spec uit een Delphi-klasse

Als je het API-contract liever in Delphi schrijft en de spec laat genereren, voorzie je een klasse van RTTI-attributen. TsgcOpenAPICodeFirstScanner doorloopt de klasse, bouwt een compleet OpenAPI 3.0 JSON-document op, en dat laad je in de server met LoadFromString. Dit vereist Delphi XE7 of nieuwer (voor uitgebreide RTTI).

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;

De methode-bodies zijn stubs — ze bestaan alleen zodat de compiler er RTTI voor genereert. Het echte werk gebeurt in OnRequest, gedispatched door het operationId dat de scanner afleidt uit elke methodenaam (ListTasks, CreateTask, GetTask…).

Geef de klasse bij het opstarten aan de scanner en laad de gegenereerde spec in de server:

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;

De attributen dekken de meest voorkomende metadata: sgcServiceContract vult het OpenAPI info-blok, sgcRoute stelt het pad in op klasse- of methodeniveau, sgcHttpGet / Post / Put / Delete / Patch / Head / Options kiest het werkwoord, sgcSummary en sgcDescription documenteren de operatie, sgcTag groepeert deze in Swagger UI, sgcResponse(code, description) declareert elk antwoord, en sgcFromPath / FromQuery / FromBody / FromHeader samen met sgcRequired beschrijven elke parameter.

Configuratie — OpenAPIOptions

Alle server-side configuratie zit onder OpenAPIOptions op de API-component, gegroepeerd in vijf sub-opties. Deze drie bevatten de dagelijkse instellingen:

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;

Met validatie ingeschakeld wordt elk binnenkomend verzoek gecontroleerd aan de hand van de JSON-Schemas die in de spec zijn gedeclareerd voordat het je handler bereikt — verplichte velden, types, formaten, enums, bereiken. Mislukkingen activeren de OnValidationError-event met de lijst met fouten en een vlag om het verzoek te accepteren of te weigeren.

Events

Zes events dekken de request-levenscyclus.

OnBeforeRequest: wordt geactiveerd vóór dispatch; stel Accept := False in om te weigeren met een 403 Forbidden. Handig voor rate-limiting, logging of per-route-gates.

OnAuthenticate: wordt geactiveerd vóór de hoofdhandler; stel Authenticated := False in om te weigeren met 401 Unauthorized. Inspecteer headers, cookies of query-parameters om te beslissen.

OnValidationError: wordt geactiveerd wanneer validatie mislukt; ontvangt de lijst met fouten. Stel Continue := False in om te weigeren met 400 Bad Request.

OnRequest: de hoofd-dispatch-event. Bekijk aOperationId, schrijf het antwoord in aContext.Response, stel Handled := True in.

OnAfterRequest: wordt geactiveerd nadat de handler is teruggekeerd — ideaal voor metrics of audit-logging.

OnException: wordt geactiveerd als een onafgehandelde uitzondering uit je handler komt. Pas aResponseCode aan als je iets anders wilt dan 500 Internal Server Error.

De twee overige sub-opties voegen hun eigen events toe: Security stuurt OnValidateAPIKey, OnValidateBasic en OnValidateBearer aan voor de securitySchemes die de spec declareert, en Mock beantwoordt een operatie zonder handler vanuit de eigen voorbeelden van de spec.

Demo's

Twee complete demo's worden met sgcOpenAPI 2026.6 meegeleverd, beide gehost door het standalone duo, zodat geen sgcWebSockets-installatie nodig is:

Upgraden

Als je momenteel TsgcWSServer_API_OpenAPI met sgcWebSockets gebruikt, verandert er niets. De klasse, de eigenschappen en de events blijven allemaal behouden, en de implementatie delegeert naar de gedeelde engine. TsgcWSAPIServer_OpenAPI is de gepubliceerde afstammeling van diezelfde klasse, dus het enige dat sgcOpenAPI verandert is waar het package vandaan komt.

sgcOpenAPI 2026.6 zal in juni beschikbaar zijn op de downloadpagina.

Vragen, feedback of hulp bij migratie? Neem contact op — je krijgt antwoord van de mensen die de code hebben geschreven.