La prossima versione di sgcOpenAPI, la 2026.6, prevista per giugno, introduce un server OpenAPI 3.0 che puoi ospitare senza installare sgcWebSockets. Sono due i componenti coinvolti: TsgcHTTPServer, il server HTTP basato su Indy, e TsgcWSAPIServer_OpenAPI, il componente API che vi colleghi. Punta il componente API a una specifica (o generala da una classe Delphi con attributi RTTI), assegna la sua proprietà Server, avvia il server HTTP e avrai una REST API documentata con Swagger UI servita automaticamente.
La novità principale è che sgcOpenAPI non ha più bisogno di sgcWebSockets per ospitare un server HTTP. Entrambi i componenti sono distribuiti, impacchettati e installati interamente da sgcOpenAPI. Se usi già sgcWebSockets, il componente TsgcWSServer_API_OpenAPI che già conosci continua a funzionare senza modifiche, perché è la stessa classe: entrambi i prodotti lo costruiscono a partire dallo stesso engine.
Cosa ottieni
La coppia porta con sé tre elementi:
- Un server HTTP (basato su Indy),
TsgcHTTPServer, con le consuete proprietàBindings,PorteActive. - L'engine OpenAPI: parsing della specifica, routing con path template e segmenti
{paramName}, validazione JSON-Schema, CORS, gestione delle eccezioni. - Due endpoint serviti automaticamente: la specifica su
/openapi.jsone una Swagger UI su/docs. Entrambi sono attivi di default e si possono abilitare o disabilitare inOpenAPIOptions.Endpoint.
Avvio rapido — l'esempio minimo
Questo è tutto ciò che serve per ospitare un server OpenAPI funzionante con Swagger UI. Nota che il componente API non ha una propria proprietà Active: assegnare Server lo collega, e assegnare nil lo scollega mentre il server HTTP continua a funzionare.
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;
Vai su http://localhost:8080/docs per la Swagger UI e su http://localhost:8080/openapi.json per la specifica. Ogni operazione definita nella specifica viene instradata al tuo handler MyOnRequest con l'operationId risolto e un contesto della richiesta completamente costruito.
Spec-First — carica un file OpenAPI 3.0 esistente
Se hai già un file JSON OpenAPI 3.0 (Petstore, un contratto API interno, uno schema pubblico di cui vuoi fare il mock), spec-first è il modo più rapido per servirlo. LoadFromFile legge e analizza la specifica, costruisce una tabella di route dalla sezione paths e confronta ogni richiesta in arrivo con essa. Il server legge JSON, quindi converti prima un contratto YAML, per esempio con sgcOpenAPI.exe.
L'operationId di ciascuna route è la chiave di dispatch. All'interno di OnRequest gestisci una per una le operazioni:
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;
TsgcOpenAPIServerContext ti fornisce accessor tipizzati per tutti gli elementi della richiesta: PathParamAsString / PathParamAsInteger per i segmenti template, QueryParamAsString / QueryParamAsInteger / QueryParamAsBoolean con valori di default, BodyAsString / BodyAsJSON per il corpo della richiesta e HeaderValue per qualsiasi header in arrivo. Per rispondere, usa gli helper RespondJSON(code, content) e RespondError(code, title, detail), oppure imposta direttamente Response.Code, Response.ContentType e Response.Content per il controllo completo.
Code-First — genera la specifica da una classe Delphi
Se preferisci scrivere il contratto API in Delphi e lasciare che la specifica venga generata, decora una classe con attributi RTTI. TsgcOpenAPICodeFirstScanner percorre la classe, costruisce un documento JSON OpenAPI 3.0 completo e tu lo carichi nel server con LoadFromString. Richiede Delphi XE7 o successivo (per l'RTTI estesa).
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;
I corpi dei metodi sono stub — esistono solo perché il compilatore emetta l'RTTI per essi. Il vero lavoro avviene in OnRequest, dove il dispatch usa l'operationId che lo scanner deriva dal nome di ciascun metodo (ListTasks, CreateTask, GetTask…).
Passa la classe allo scanner all'avvio e carica la specifica generata nel 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;
Gli attributi coprono i metadati comuni: sgcServiceContract popola il blocco info di OpenAPI, sgcRoute imposta il path a livello di classe o di metodo, sgcHttpGet / Post / Put / Delete / Patch / Head / Options sceglie il verbo, sgcSummary e sgcDescription documentano l'operazione, sgcTag la raggruppa nella Swagger UI, sgcResponse(code, description) dichiara ciascuna risposta e sgcFromPath / FromQuery / FromBody / FromHeader insieme a sgcRequired descrivono ciascun parametro.
Configurazione — OpenAPIOptions
Tutta la configurazione lato server risiede in OpenAPIOptions sul componente API, raggruppata in cinque sotto-opzioni. Queste tre riguardano le impostazioni di uso quotidiano:
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 validazione attiva, ogni richiesta in arrivo viene controllata rispetto ai JSON Schema dichiarati nella specifica prima di raggiungere il tuo handler — campi obbligatori, tipi, formati, enum, intervalli. In caso di errore viene scatenato l'evento OnValidationError con l'elenco degli errori e un flag per accettare o rifiutare la richiesta.
Eventi
Sei eventi coprono il ciclo di vita della richiesta:
OnBeforeRequest: scatta prima del dispatch; imposta Accept := False per rifiutare con un 403 Forbidden. Utile per rate-limiting, logging o gate per singola route.
OnAuthenticate: scatta prima dell'handler principale; imposta Authenticated := False per rifiutare con 401 Unauthorized. Esamina header, cookie o parametri di query per decidere.
OnValidationError: scatta quando la validazione fallisce; riceve l'elenco degli errori. Imposta Continue := False per rifiutare con 400 Bad Request.
OnRequest: l'evento principale di dispatch. Guarda aOperationId, scrivi la risposta in aContext.Response, imposta Handled := True.
OnAfterRequest: scatta dopo che l'handler è ritornato — ideale per metriche o audit logging.
OnException: scatta se un'eccezione non gestita esce dal tuo handler. Modifica aResponseCode se vuoi qualcosa di diverso da 500 Internal Server Error.
I restanti due sotto-oggetti aggiungono i propri: Security pilota OnValidateAPIKey, OnValidateBasic e OnValidateBearer per i securitySchemes dichiarati dalla spec, e Mock risponde a un'operazione senza handler usando gli esempi propri della spec.
Demo
Due demo complete sono incluse con sgcOpenAPI 2026.6, entrambe ospitate dalla coppia standalone, quindi non è richiesta alcuna installazione di sgcWebSockets:
- Demos/30.Server/01.OpenAPI_Server_CodeFirst, una Task Manager API definita interamente con attributi su una classe Delphi.
- Demos/30.Server/02.OpenAPI_Server_SpecFirst, il classico esempio Petstore, servito da
petstore.json.
Aggiornamento
Se attualmente usi TsgcWSServer_API_OpenAPI con sgcWebSockets, non cambia nulla. La classe, le sue proprietà e i suoi eventi sono tutti preservati, e l'implementazione delega all'engine condiviso. TsgcWSAPIServer_OpenAPI è il discendente pubblicato di quella stessa classe, quindi l'unica cosa che sgcOpenAPI cambia è da dove proviene il package.
sgcOpenAPI 2026.6 sarà disponibile sulla pagina dei download a giugno.
Domande, feedback o aiuto per la migrazione? Mettiti in contatto — riceverai una risposta dalle persone che hanno scritto il codice.
