Free Pascal non è soltanto un linguaggio per applicazioni desktop: grazie a framework come Horse — ispirato a Express.js per Node.js — è possibile scrivere un server REST completo con poche righe di codice, gestendo rotte, middleware e risposte JSON in modo dichiarativo. Horse funziona sia con Delphi sia con Free Pascal/Lazarus, appoggiandosi rispettivamente a Indy o ai binding HTTP di FPC.
Definire le rotte con THorse
La classe THorse espone metodi statici che ricalcano i verbi HTTP: Get, Post, Put, Delete. Ogni rotta riceve una procedura con la firma (Req: THorseRequest; Res: THorseResponse; Next: TNextProc), lo stesso pattern middleware/handler che si trova in molti framework web.
program ApiServer;
{$mode objfpc}{$H+}
uses
SysUtils, Classes, fpjson, Horse;
procedure GetPersonaHandler(Req: THorseRequest; Res: THorseResponse; Next: TNextProc);
var
Nome: string;
begin
Nome := Req.Params['nome'];
Res.Send('{"nome":"' + Nome + '"}').Status(200);
end;
begin
THorse.Get('/persona/:nome', GetPersonaHandler);
THorse.Listen(9000);
end.Un middleware di autenticazione
Registrando più handler sulla stessa rotta, il primo può fare da guardia: se non chiama Next, la richiesta si ferma lì. È il pattern classico per un controllo di autenticazione basato su header Authorization: Bearer ....
procedure Autentica(Req: THorseRequest; Res: THorseResponse; Next: TNextProc);
var
IntestazioneAuth, Token: string;
begin
IntestazioneAuth := Req.Headers['Authorization'];
if (IntestazioneAuth <> '') and (Pos('Bearer ', IntestazioneAuth) = 1) then
begin
Token := Copy(IntestazioneAuth, 8, Length(IntestazioneAuth));
if VerificaToken(Token) then
begin
Next;
Exit;
end;
end;
Res.Send('{"result":false,"message":"Unauthorized"}').Status(401);
end;
begin
THorse.Get('/persona', Autentica);
THorse.Get('/persona', GetListaPersoneHandler);
THorse.Listen(9000);
end.Nell'esempio da cui è tratto questo pattern, il token viene generato con una firma calcolata "in casa" (hash più codifica base64url), un esercizio utile per capire la struttura di un JWT — header, payload e firma separati da un punto — ma non un'implementazione da usare così com'è in produzione: per un'API reale conviene appoggiarsi a una libreria JWT matura, con un algoritmo di firma verificato (HMAC-SHA256 vero, non un hash "fatto in casa"), e a un secret conservato in un file di configurazione, mai scritto in chiaro nel sorgente.
Serializzare le risposte con fpjson
La unit fpjson (accompagnata da jsonparser per la lettura) fornisce le classi TJSONObject e TJSONArray per costruire e leggere payload JSON senza dipendenze esterne. Un pattern comodo è centralizzare la busta di risposta in un'unica funzione:
procedure InviaRisposta(Res: THorseResponse; Status: Integer;
Successo: Boolean; const Messaggio: string; Valore: TJSONData = nil);
var
Risposta: TJSONObject;
begin
Risposta := TJSONObject.Create;
try
Risposta.Add('result', TJSONBoolean.Create(Successo));
Risposta.Add('message', TJSONString.Create(Messaggio));
if Assigned(Valore) then
Risposta.Add('value', Valore);
Res.Send(Risposta.AsJSON).Status(Status);
finally
Risposta.Free;
end;
end;Con questi tre ingredienti — rotte, middleware e busta JSON uniforme — si ottiene rapidamente un'API coerente, testabile con qualunque client HTTP, incluso un semplice script PHP con cURL, come vedremo nel prossimo articolo.