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

· Releases

De volgende release van sgcOpenAPI — versie 2026.6, verwacht in juni — bevat een gloednieuwe component: TsgcHTTPServer_OpenAPI. Het is één zelfstandige Delphi-component die een OpenAPI 3.0-service host met een ingebouwde HTTP-server. Plaats het op een formulier, wijs het naar een spec (of genereer er een uit een Delphi-klasse met RTTI-attributen), stel Active := True in, 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. De nieuwe component wordt volledig vanuit sgcOpenAPI geleverd, verpakt en geïnstalleerd. Als je sgcWebSockets al gebruikt, blijft de bestaande TsgcWSServer_API_OpenAPI-component ongewijzigd werken — beide delen intern dezelfde engine.

Wat je krijgt in één component

TsgcHTTPServer_OpenAPI bundelt drie zaken:

Snelstart — het minimale voorbeeld

Dit is alles wat je nodig hebt om een werkende OpenAPI-server met Swagger UI te hosten:

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;

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- of YAML-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.

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

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;

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

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, gegroepeerd in drie sub-opties:

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

De component biedt zes events voor 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.

Demo's

Twee complete demo's worden met sgcOpenAPI 2026.6 meegeleverd, beide met uitsluitend de nieuwe standalone component — geen sgcWebSockets-installatie vereist:

Upgraden

Als je momenteel TsgcWSServer_API_OpenAPI met sgcWebSockets gebruikt, verandert er niets — de klasse, eigenschappen en events blijven allemaal behouden en de implementatie delegeert nu naar dezelfde gedeelde engine die de nieuwe standalone component aandrijft. Je kunt je bestaande code zo laten, of formulier voor formulier migreren naar TsgcHTTPServer_OpenAPI om de sgcWebSockets-afhankelijkheid te verwijderen.

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.