Dans l'approche Code-First, la classe Delphi est la source de vérité : les méthodes, paramètres et attributs RTTI décrivent le contrat de l'API, et la spécification OpenAPI 3.0 en est générée au démarrage. Une fois générée, la spécification est chargée dans le serveur avec LoadFromString et la table de routage est construite comme si elle provenait d'un fichier.
Code-First nécessite Delphi XE7 ou ultérieur car il s'appuie sur le nouveau RTTI pour inspecter les attributs. La fonctionnalité réside dans l'unité sgcHTTP_OpenAPI_Server_CodeFirst et est protégée par la directive de compilation {$IFDEF DXE7}.
Annotez une classe Delphi avec des attributs pour décrire l'API. Les corps des méthodes sont des stubs : la logique réelle va dans l'événement OnRequest du serveur ; les méthodes n'existent que pour que le scanner RTTI puisse lire leurs signatures.
[sgcServiceContract('Task Manager API', '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')]
[sgcResponse(201, 'Task created')]
procedure CreateTask([sgcFromBody] const body: string); virtual;
[sgcHttpGet][sgcRoute('/tasks/{taskId}')]
[sgcResponse(200, 'Task')]
[sgcResponse(404, 'Not found')]
procedure GetTask([sgcFromPath][sgcRequired] const taskId: Integer); virtual;
end;
Les attributs suivants sont reconnus par le scanner. Les attributs au niveau classe décrivent le service dans son ensemble ; les attributs au niveau méthode décrivent une opération unique ; les attributs au niveau paramètre décrivent d'où provient la valeur.
sgcServiceContract(name, description, version) : niveau classe. Remplit le bloc info OpenAPI.
sgcRoute(path) : chemin de base au niveau classe, ou route au niveau méthode relative au chemin de base de la classe.
sgcHttpGet, sgcHttpPost, sgcHttpPut, sgcHttpDelete, sgcHttpPatch, sgcHttpHead, sgcHttpOptions : niveau méthode. Déclarent le verbe HTTP de l'opération.
sgcSummary(text) : niveau méthode. Résumé court affiché dans la spécification et la Swagger UI.
sgcDescription(text) : niveau méthode. Description longue pour la spécification.
sgcTag(name) : niveau méthode. Regroupe les opérations sous la même étiquette dans la Swagger UI.
sgcResponse(code, description) : niveau méthode. Déclare une réponse avec son code de statut HTTP et sa description. Peut être répété pour déclarer plusieurs réponses.
sgcFromPath, sgcFromQuery, sgcFromBody, sgcFromHeader : niveau paramètre. Déclarent d'où le paramètre est lu.
sgcRequired : niveau paramètre. Marque le paramètre comme obligatoire ; les paramètres manquants sont signalés par la validation lorsque ValidateRequired est activé.
Utilisez TsgcOpenAPICodeFirstScanner pour générer la spécification OpenAPI 3.0 à partir de la classe annotée, puis chargez le résultat dans le serveur.
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;
Le scanner dérive l'operationId du nom de la méthode : la méthode ListTasks devient l'opération ListTasks. Effectuez le dispatch dans OnRequest en utilisant le même operationId.
procedure TForm1.MyOnRequest(Sender: TObject; const aOperationId: string;
const aContext: TsgcOpenAPIServerContext; var Handled: Boolean);
begin
if aOperationId = 'ListTasks' then
aContext.RespondJSON(200, '[]')
else if aOperationId = 'CreateTask' then
aContext.RespondJSON(201, '{"id":1}')
else if aOperationId = 'GetTask' then
aContext.RespondJSON(200, '{"id":' +
IntToStr(aContext.PathParamAsInteger('taskId')) + '}')
else
aContext.RespondError(404, 'Not Found', 'Unknown operation');
Handled := True;
end;
Un exemple code-first complet est fourni sous Demos/30.Server/01.CodeFirst/.