L'édition Enterprise de sgcWebSockets propose un nouveau composant, TsgcWSAPIServer_OpenAPI, qui transforme une description OpenAPI 3 en serveur REST opérationnel dans votre application Delphi. Déposez-le sur une fiche, reliez-le à un serveur HTTP, fournissez-lui une spécification — et les routes, la validation des requêtes, les réponses d'erreur et la documentation Swagger UI en direct sont câblées pour vous. Cet article explique comment fonctionne le composant, les deux façons de le piloter (spec-first et code-first), les options de configuration qui comptent, ainsi qu'un exemple Delphi complet que vous pouvez coller directement dans un nouveau projet.
Ce que fait le composant
TsgcWSAPIServer_OpenAPI est un serveur d'API léger qui se branche sur TsgcWebSocketHTTPServer via le point d'extension API-server existant. Vous l'attachez à un serveur, vous chargez une spécification OpenAPI 3.0, et il effectue quatre opérations à chaque requête HTTP entrante :
- il fait correspondre l'URL et la méthode HTTP aux routes déclarées dans la spécification (paramètres
{path}compris) ; - il valide la requête — champs obligatoires, paramètres de requête, paramètres de chemin et corps de la requête — par rapport aux schémas de la spécification ;
- il déclenche un événement
OnRequestavec l'operationIdrésolu et un objet de contexte entièrement renseigné, pour que votre code n'ait plus qu'à écrire la logique métier ; - il sert deux endpoints intégrés prêts à l'emploi — la spécification brute sur
/openapi.jsonet un Swagger UI interactif sur/docs— avec préflight CORS optionnel sur chaque route.
Résultat : la spécification devient l'unique source de vérité. Changez un chemin, un paramètre ou un code de réponse dans le JSON, redémarrez, et le serveur récupère le nouveau contrat sans recompiler le code Delphi.
Spec-first : charger un fichier OpenAPI 3 existant
Si vous disposez déjà d'un document OpenAPI 3 (par exemple un petstore.json exporté depuis un concepteur d'API), le câblage tient essentiellement en trois lignes — créer le composant, charger la spécification, attacher un serveur. Tout le reste relève de la configuration et du gestionnaire OnRequest qui produit les réponses réelles.
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'événement OnRequest est distribué par operationId — la chaîne que vous avez écrite à côté de chaque opération dans le YAML/JSON. Vous écrivez une branche par opération, vous lisez les entrées depuis le contexte et vous émettez une réponse :
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'objet TsgcOpenAPIServerContext est la pièce maîtresse de chaque gestionnaire. Il expose les paramètres de chemin et de requête par leur nom (PathParamAsString, PathParamAsInteger, QueryParamAsString, QueryParamAsInteger, QueryParamAsBoolean), la consultation des en-têtes via HeaderValue, le corps sous forme de chaîne (BodyAsString) ou de JSON pré-analysé (BodyAsJSON), ainsi que deux aides à la réponse : RespondJSON(code, content) pour une charge utile classique et RespondError(code, title, detail), qui produit un corps application/problem+json conforme à la RFC 7807 pour que les clients obtiennent toujours une forme d'erreur cohérente.
Code-first : générer la spécification à partir d'attributs Delphi
Le second mode est le flux inverse : vous déclarez votre API sous forme de classe Delphi décorée d'attributs, vous demandez au scanner d'émettre le document OpenAPI à l'exécution, et vous réinjectez ce document dans le même composant. Il n'y a rien à écrire à la main en 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 elle-même n'a pas besoin de vrais corps de méthodes — le scanner la lit via la RTTI, et la logique réelle reste dans votre gestionnaire OnRequest. Générer la spécification et démarrer le serveur tient en quelques lignes :
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;
L'ensemble d'attributs couvre les cas courants : routage (sgcHttpGet, sgcHttpPost, sgcHttpPut, sgcHttpDelete, sgcHttpPatch, sgcRoute), liaison de paramètres (sgcFromPath, sgcFromQuery, sgcFromHeader, sgcFromBody), validation (sgcRequired, sgcMinLength, sgcMaxLength, sgcRange, sgcPattern) et documentation (sgcSummary, sgcDescription, sgcTag, sgcResponse).
Configuration détaillée
OpenAPIOptions est regroupé en trois sous-objets persistants pour que vous puissiez tous les voir dans l'Inspecteur d'objets :
Endpoint—BasePathajoute un préfixe à chaque route ainsi qu'aux endpoints intégrés/openapi.jsonet/docs;SpecFilepeut être utilisé à la conception comme alternative àLoadFromFile;ServeSpecetServeSwaggerUIactivent ou désactivent les deux endpoints intégrés (activés tous deux par défaut).Validation— l'interrupteur principal estValidateRequest;ValidateRequired,ValidateQueryParams,ValidatePathParamsetValidateRequestBodyvous permettent de restreindre ce qui est vérifié. En cas d'échec de la validation,OnValidationErrorest déclenché avec la liste des problèmes et un indicateurContinue— mettez-le àFalsepour rejeter la requête automatiquement.CORS— positionnezEnabled := Trueet le serveur répondra aux requêtes préflightOPTIONSsur chaque route, en utilisantAllowOrigins,AllowHeadersetAllowMethodscomme politique de réponse.
Au-delà de OnRequest, quatre autres événements permettent de vous greffer sur le pipeline : OnBeforeRequest (positionnez Accept := False pour court-circuiter, utile pour la limitation de débit ou la journalisation), OnAfterRequest (post-traitement une fois la réponse produite), OnAuthenticate (positionnez Authenticated := True après avoir vérifié un jeton ou une session) et OnException (filet de sécurité global qui vous laisse modifier le statut HTTP avant que le framework n'écrive le corps d'erreur).
Swagger UI prêt à l'emploi
Avec ServeSwaggerUI := True, le serveur publie une page Swagger UI sur BasePath + '/docs' qui charge la spécification depuis BasePath + '/openapi.json'. Ouvrez l'URL dans un navigateur et vous obtenez l'expérience standard « try-it-out », alimentée par votre propre serveur en cours d'exécution — pas de build de documentation séparé, pas d'export statique. Combiné à CORS, c'est le moyen le plus rapide de remettre un backend à une équipe frontend ou à un partenaire qui s'intègre à votre API.
Pour l'obtenir
Le composant fait partie de l'édition Enterprise de sgcWebSockets, enregistré sur la page de palette SGC OpenAPI. Deux démos complètes — l'une en spec-first à partir d'un JSON Petstore, l'autre en code-first à partir d'un service de gestion de tâches — sont livrées dans Demos\23.OpenAPI. Téléchargez la dernière version depuis la page de téléchargement de sgcWebSockets.
Des questions, des retours ou besoin d'aide pour intégrer le composant dans un projet existant ? Contactez-nous — vous recevrez une réponse des personnes qui ont écrit le code.
