L'edizione Enterprise di sgcWebSockets include un nuovo componente, TsgcWSAPIServer_OpenAPI, che trasforma una descrizione OpenAPI 3 in un server REST funzionante all'interno della tua applicazione Delphi. Lo trascini su una form, lo colleghi a un server HTTP, gli fornisci una specifica — e le rotte, la validazione delle richieste, le risposte di errore e la documentazione live di Swagger UI vengono configurate automaticamente. Questo articolo illustra come funziona il componente, i due modi in cui puoi utilizzarlo (spec-first e code-first), le opzioni di configurazione importanti e un esempio Delphi completo che puoi incollare direttamente in un nuovo progetto.
Cosa fa il componente
TsgcWSAPIServer_OpenAPI è un server API leggero che si integra con TsgcWebSocketHTTPServer tramite il punto di estensione API-server esistente. Lo colleghi a un server, carichi una specifica OpenAPI 3.0, e fa quattro cose ad ogni richiesta HTTP in entrata:
- confronta l'URL e il metodo HTTP con le rotte dichiarate nella specifica (inclusi i parametri
{path}); - valida la richiesta — campi obbligatori, parametri di query, parametri di percorso e corpo della richiesta — rispetto agli schemi nella specifica;
- attiva un evento
OnRequestcon l'operationIdrisolto e un oggetto contesto completamente popolato, in modo che il tuo codice debba scrivere solo la logica di business; - serve due endpoint integrati pronti all'uso — la specifica grezza in
/openapi.jsone una Swagger UI interattiva in/docs— con preflight CORS opzionale su ogni rotta.
Il risultato è che la specifica diventa l'unica fonte di verità: cambia un percorso, un parametro o un codice di risposta nel JSON, riavvia, e il server adotta il nuovo contratto senza ricompilare il codice Delphi.
Spec-first: caricare un file OpenAPI 3 esistente
Se hai già un documento OpenAPI 3 (ad esempio un petstore.json esportato da un designer di API) il collegamento è essenzialmente di tre righe — crea il componente, carica la specifica, collega un server. Tutto il resto è configurazione e il gestore OnRequest che produce le risposte effettive.
uses
sgcWebSocket, sgcWebSocket_Classes,
sgcWebSocket_Server_API_OpenAPI,
sgcHTTP_OpenAPI_Server;
var
WSServer: TsgcWebSocketHTTPServer;
FOpenAPI: TsgcWSAPIServer_OpenAPI;
begin
WSServer := TsgcWebSocketHTTPServer.Create(nil);
WSServer.Port := 8080;
FOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
FOpenAPI.OnRequest := OnOpenAPIRequest;
FOpenAPI.OnBeforeRequest := OnOpenAPIBeforeRequest;
FOpenAPI.OnValidationError := OnOpenAPIValidationError;
// Configuration
FOpenAPI.OpenAPIOptions.Endpoint.ServeSpec := True;
FOpenAPI.OpenAPIOptions.Endpoint.ServeSwaggerUI := True;
FOpenAPI.OpenAPIOptions.CORS.Enabled := True;
FOpenAPI.OpenAPIOptions.Validation.ValidateRequest := True;
FOpenAPI.OpenAPIOptions.Validation.ValidateRequired := True;
FOpenAPI.OpenAPIOptions.Validation.ValidateRequestBody := True;
// Load spec and attach to server
FOpenAPI.LoadFromFile('petstore.json');
FOpenAPI.Server := WSServer;
WSServer.Active := True;
// Swagger UI: http://localhost:8080/docs
// Raw spec: http://localhost:8080/openapi.json
end;
L'evento OnRequest viene inviato per ogni operationId — la stringa che hai scritto accanto a ciascuna operazione nel YAML/JSON. Scrivi un ramo per ogni operazione, leggi gli input dal contesto ed emetti una risposta:
procedure TForm1.OnOpenAPIRequest(Sender: TObject;
const aOperationId: string; const aContext: TsgcOpenAPIServerContext;
var Handled: Boolean);
var
vId: Int64;
vLimit: Integer;
begin
Handled := True;
if aOperationId = 'listPets' then
begin
vLimit := aContext.QueryParamAsInteger('limit', 100);
aContext.RespondJSON(200, BuildPetsJSON(vLimit));
end
else if aOperationId = 'getPetById' then
begin
vId := aContext.PathParamAsInteger('petId');
if FindPet(vId) then
aContext.RespondJSON(200, PetAsJSON(vId))
else
aContext.RespondError(404, 'Not Found',
Format('Pet %d not found', [vId]));
end
else
Handled := False;
end;
L'oggetto TsgcOpenAPIServerContext è il cavallo da tiro di ogni gestore. Espone i parametri di percorso e di query per nome (PathParamAsString, PathParamAsInteger, QueryParamAsString, QueryParamAsInteger, QueryParamAsBoolean), la ricerca degli header tramite HeaderValue, il corpo come stringa (BodyAsString) o JSON già analizzato (BodyAsJSON), più due helper di risposta: RespondJSON(code, content) per un payload normale e RespondError(code, title, detail), che produce un corpo application/problem+json in stile RFC 7807 affinché i client ottengano sempre una forma di errore coerente.
Code-first: generare la specifica dagli attributi Delphi
La seconda modalità è il flusso inverso: dichiari la tua API come una classe Delphi decorata con attributi, chiedi allo scanner di emettere il documento OpenAPI a runtime, e reinserisci quel documento nello stesso componente. Non c'è nulla da scrivere a mano in JSON.
uses
sgcHTTP_OpenAPI_Server_CodeFirst;
type
[sgcServiceContract('Task Manager API',
'A simple task manager', '1.0.0')]
[sgcRoute('/api/v1')]
TTaskService = class
public
[sgcHttpGet]
[sgcRoute('/tasks')]
[sgcSummary('List all tasks')]
[sgcResponse(200, 'A list of tasks')]
procedure ListTasks([sgcFromQuery] const status: string); virtual;
[sgcHttpGet]
[sgcRoute('/tasks/{taskId}')]
[sgcSummary('Get a task by ID')]
[sgcResponse(200, 'The requested task')]
[sgcResponse(404, 'Task not found')]
procedure GetTask([sgcFromPath][sgcRequired]
const taskId: Integer); virtual;
[sgcHttpPost]
[sgcRoute('/tasks')]
[sgcSummary('Create a new task')]
[sgcResponse(201, 'Task created')]
procedure CreateTask([sgcFromBody] const body: string); virtual;
end;
La classe stessa non necessita di corpi reali — lo scanner la legge tramite RTTI, la logica vera e propria vive ancora nel tuo gestore OnRequest. Generare la specifica e avviare il server richiede poche righe:
var
oScanner: TsgcOpenAPICodeFirstScanner;
vSpec: string;
begin
oScanner := TsgcOpenAPICodeFirstScanner.Create;
try
vSpec := oScanner.GenerateSpec(TTaskService);
finally
oScanner.Free;
end;
FOpenAPI.LoadFromString(vSpec);
FOpenAPI.Server := WSServer;
WSServer.Active := True;
end;
Il set di attributi copre i casi più comuni: routing (sgcHttpGet, sgcHttpPost, sgcHttpPut, sgcHttpDelete, sgcHttpPatch, sgcRoute), binding dei parametri (sgcFromPath, sgcFromQuery, sgcFromHeader, sgcFromBody), validazione (sgcRequired, sgcMinLength, sgcMaxLength, sgcRange, sgcPattern) e documentazione (sgcSummary, sgcDescription, sgcTag, sgcResponse).
Configurazione in dettaglio
OpenAPIOptions è raggruppato in tre sotto-oggetti persistenti in modo da poterli vedere tutti nell'Object Inspector:
Endpoint—BasePathantepone un prefisso a ogni rotta e agli endpoint integrati/openapi.jsone/docs;SpecFilepuò essere usato a design time come alternativa aLoadFromFile;ServeSpeceServeSwaggerUIattivano/disattivano i due endpoint integrati (entrambi attivi per impostazione predefinita).Validation— l'interruttore principale èValidateRequest;ValidateRequired,ValidateQueryParams,ValidatePathParamseValidateRequestBodyti permettono di restringere ciò che viene controllato. Quando la validazione fallisce,OnValidationErrorsi attiva con l'elenco dei problemi e un flagContinue— impostalo aFalseper rifiutare automaticamente la richiesta.CORS— impostaEnabled := Truee il server risponderà alle richieste preflightOPTIONSper ogni rotta, utilizzandoAllowOrigins,AllowHeaderseAllowMethodscome politica di risposta.
Oltre a OnRequest, altri quattro eventi ti permettono di intervenire nella pipeline: OnBeforeRequest (imposta Accept := False per interrompere, utile per limitazione di frequenza o logging), OnAfterRequest (post-elaborazione una volta prodotta una risposta), OnAuthenticate (imposta Authenticated := True dopo aver verificato un token o una sessione) e OnException (catch-all che ti permette di cambiare lo stato HTTP prima che il framework scriva il corpo dell'errore).
Swagger UI pronto all'uso
Con ServeSwaggerUI := True il server pubblica una pagina Swagger UI in BasePath + '/docs' che carica la specifica da BasePath + '/openapi.json'. Apri l'URL in un browser e ottieni l'esperienza standard try-it-out, alimentata dal tuo server in esecuzione — nessuna build di documentazione separata, nessuna esportazione statica. Combinato con CORS, è il modo più veloce per consegnare un backend a un team frontend o a un partner che si integra con la tua API.
Come ottenerlo
Il componente fa parte dell'edizione Enterprise di sgcWebSockets, registrato nella pagina della palette SGC OpenAPI. Due demo complete — una spec-first che utilizza un JSON Petstore, una code-first che utilizza un servizio di task manager — sono incluse in Demos\23.OpenAPI. Scarica l'ultima build dalla pagina di download di sgcWebSockets.
Domande, feedback o aiuto per integrarlo in un progetto esistente? Contattaci — otterrai una risposta dalle persone che hanno scritto il codice.
