Die Enterprise-Edition von sgcWebSockets enthält eine neue Komponente, TsgcWSAPIServer_OpenAPI, die eine OpenAPI-3-Beschreibung in einen laufenden REST-Server innerhalb Ihrer Delphi-Anwendung verwandelt. Legen Sie sie auf ein Formular, verbinden Sie sie mit einem HTTP-Server, übergeben Sie ihr eine Spezifikation — und die Routen, die Anfragevalidierung, die Fehlerantworten und die Live-Swagger-UI-Dokumentation werden für Sie verdrahtet. Dieser Beitrag erläutert, wie die Komponente funktioniert, die beiden Möglichkeiten, sie zu steuern (spezifikationsbasiert und codebasiert), die wichtigsten Konfigurationsschalter und ein vollständiges Delphi-Beispiel, das Sie direkt in ein neues Projekt einfügen können.
Was die Komponente leistet
TsgcWSAPIServer_OpenAPI ist ein schlanker API-Server, der sich über den vorhandenen API-Server-Erweiterungspunkt in TsgcWebSocketHTTPServer einklinkt. Sie hängen ihn an einen Server an, laden eine OpenAPI-3.0-Spezifikation, und er führt bei jeder eingehenden HTTP-Anfrage vier Dinge aus:
- er gleicht die URL und die HTTP-Methode mit den in der Spezifikation deklarierten Routen ab (einschließlich
{path}-Parameter); - er validiert die Anfrage — Pflichtfelder, Query-Parameter, Pfadparameter und Anfrage-Body — gegen die Schemata in der Spezifikation;
- er löst ein
OnRequest-Ereignis mit der aufgelöstenoperationIdund einem vollständig befüllten Kontextobjekt aus, sodass Ihr Code nur die Geschäftslogik schreiben muss; - er stellt von Haus aus zwei integrierte Endpunkte bereit — die rohe Spezifikation unter
/openapi.jsonund eine interaktive Swagger UI unter/docs— mit optionalem CORS-Preflight auf jeder Route.
Das Ergebnis ist, dass die Spezifikation zur einzigen verbindlichen Quelle wird: Ändern Sie einen Pfad, einen Parameter oder einen Antwortcode im JSON, starten Sie neu, und der Server übernimmt den neuen Vertrag, ohne dass Delphi-Code neu kompiliert werden muss.
Spezifikationsbasiert: eine bestehende OpenAPI-3-Datei laden
Wenn Sie bereits ein OpenAPI-3-Dokument haben (zum Beispiel eine aus einem API-Designer exportierte petstore.json), umfasst die Einrichtung im Wesentlichen drei Zeilen — Komponente erstellen, Spezifikation laden, Server anhängen. Alles Weitere ist Konfiguration und der OnRequest-Handler, der die eigentlichen Antworten erzeugt.
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;
Das OnRequest-Ereignis wird pro operationId ausgelöst — der Zeichenkette, die Sie neben jeder Operation im YAML/JSON eingetragen haben. Sie schreiben einen Zweig pro Operation, lesen die Eingaben aus dem Kontext und geben eine Antwort aus:
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;
Das Objekt TsgcOpenAPIServerContext ist das Arbeitstier jedes Handlers. Es stellt Pfad- und Query-Parameter über den Namen bereit (PathParamAsString, PathParamAsInteger, QueryParamAsString, QueryParamAsInteger, QueryParamAsBoolean), Header-Zugriff über HeaderValue, den Body als Zeichenkette (BodyAsString) oder als vorgeparstes JSON (BodyAsJSON), dazu zwei Antwort-Hilfsmethoden: RespondJSON(code, content) für eine normale Nutzlast und RespondError(code, title, detail), das einen application/problem+json-Body im Stil von RFC 7807 erzeugt, sodass Clients stets eine einheitliche Fehlerform erhalten.
Codebasiert: die Spezifikation aus Delphi-Attributen generieren
Der zweite Modus ist der umgekehrte Ablauf: Sie deklarieren Ihre API als Delphi-Klasse mit Attributen, lassen den Scanner das OpenAPI-Dokument zur Laufzeit ausgeben und führen dieses Dokument der gleichen Komponente wieder zu. Es gibt nichts, was von Hand in JSON geschrieben werden müsste.
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;
Die Klasse selbst benötigt keine echten Methodenrümpfe — der Scanner liest sie über RTTI aus, die eigentliche Logik liegt weiterhin in Ihrem OnRequest-Handler. Die Spezifikation zu erzeugen und den Server zu starten erfordert nur wenige Zeilen:
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;
Der Attributsatz deckt die häufigen Fälle ab: Routing (sgcHttpGet, sgcHttpPost, sgcHttpPut, sgcHttpDelete, sgcHttpPatch, sgcRoute), Parameterbindung (sgcFromPath, sgcFromQuery, sgcFromHeader, sgcFromBody), Validierung (sgcRequired, sgcMinLength, sgcMaxLength, sgcRange, sgcPattern) und Dokumentation (sgcSummary, sgcDescription, sgcTag, sgcResponse).
Konfiguration im Detail
OpenAPIOptions ist in drei persistente Unterobjekte gegliedert, damit Sie alle im Objektinspektor sehen können:
Endpoint—BasePathstellt jeder Route sowie den integrierten Endpunkten/openapi.jsonund/docsein Präfix voran;SpecFilekann zur Entwurfszeit als Alternative zuLoadFromFileverwendet werden;ServeSpecundServeSwaggerUIschalten die beiden integrierten Endpunkte (beide standardmäßig aktiviert).Validation— der Hauptschalter istValidateRequest; mitValidateRequired,ValidateQueryParams,ValidatePathParamsundValidateRequestBodykönnen Sie eingrenzen, was geprüft wird. Schlägt die Validierung fehl, wirdOnValidationErrormit der Liste der Probleme und einemContinue-Flag ausgelöst — setzen Sie es aufFalse, um die Anfrage automatisch abzuweisen.CORS— setzen SieEnabled := True, und der Server beantwortet Preflight-OPTIONS-Anfragen für jede Route, wobeiAllowOrigins,AllowHeadersundAllowMethodsals Antwortrichtlinie verwendet werden.
Neben OnRequest erlauben vier weitere Ereignisse, sich in die Pipeline einzuklinken: OnBeforeRequest (setzen Sie Accept := False, um abzukürzen, nützlich für Ratenbegrenzung oder Protokollierung), OnAfterRequest (Nachverarbeitung, sobald Sie eine Antwort erzeugt haben), OnAuthenticate (setzen Sie Authenticated := True, nachdem Sie ein Token oder eine Session geprüft haben) und OnException (Auffangereignis, mit dem Sie den HTTP-Status ändern können, bevor das Framework den Fehler-Body schreibt).
Swagger UI von Haus aus
Mit ServeSwaggerUI := True veröffentlicht der Server eine Swagger-UI-Seite unter BasePath + '/docs', die die Spezifikation von BasePath + '/openapi.json' lädt. Öffnen Sie die URL in einem Browser, und Sie erhalten das standardmäßige Try-it-out-Erlebnis, gespeist aus Ihrem eigenen laufenden Server — ohne separaten Dokumentations-Build, ohne statischen Export. Kombiniert mit CORS ist dies der schnellste Weg, ein Backend an ein Frontend-Team oder an einen Partner zu übergeben, der gegen Ihre API integriert.
Bezug
Die Komponente ist Teil der Enterprise-Edition von sgcWebSockets und auf der Palettenseite SGC OpenAPI registriert. Zwei vollständige Demos — eine spezifikationsbasiert mit einer Petstore-JSON, eine codebasiert mit einem Task-Manager-Dienst — werden in Demos\23.OpenAPI mitgeliefert. Laden Sie den aktuellen Build von der sgcWebSockets-Downloadseite herunter.
Fragen, Rückmeldungen oder Hilfe bei der Einbindung in ein bestehendes Projekt? Kontakt aufnehmen — Sie erhalten eine Antwort von den Personen, die den Code geschrieben haben.
