Molti marketplace e portali verticali (annunci, veicoli, immobili) espongono — o promettono di esporre — un'API REST per i partner. Prima ancora di conoscere i dettagli specifici di un provider, è comodo avere pronto uno scheletro di classe client minimale, che gestisca la parte che non cambia mai: connessione HTTPS, header di autorizzazione, gestione delle eccezioni di rete.
La struttura base
Il punto da non dimenticare è l'IOHandler: senza TIdSSLIOHandlerSocketOpenSSL agganciato a TIdHTTP, qualunque chiamata verso un endpoint https:// fallisce con un errore di connessione poco esplicativo. Servono anche le DLL OpenSSL (libeay32.dll/ssleay32.dll o le versioni più recenti libcrypto/libssl) distribuite insieme all'eseguibile.
unit ApiClientBase;
interface
uses
System.SysUtils, System.Classes, IdHTTP, IdSSLOpenSSL;
type
TApiClientBase = class
private
FHTTP: TIdHTTP;
FBaseURL: string;
FAccessToken: string;
procedure ImpostaAutorizzazione;
public
constructor Create(const ABaseURL: string);
destructor Destroy; override;
procedure SetAccessToken(const AToken: string);
function Get(const APath: string): string;
end;
implementation
constructor TApiClientBase.Create(const ABaseURL: string);
begin
inherited Create;
FBaseURL := ABaseURL;
FHTTP := TIdHTTP.Create(nil);
FHTTP.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(FHTTP);
FHTTP.Request.ContentType := 'application/json';
end;
destructor TApiClientBase.Destroy;
begin
FHTTP.IOHandler.Free;
FHTTP.Free;
inherited;
end;
procedure TApiClientBase.SetAccessToken(const AToken: string);
begin
FAccessToken := AToken;
end;
procedure TApiClientBase.ImpostaAutorizzazione;
begin
FHTTP.Request.CustomHeaders.Clear;
if FAccessToken <> '' then
FHTTP.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + FAccessToken);
end;
function TApiClientBase.Get(const APath: string): string;
begin
ImpostaAutorizzazione;
try
Result := FHTTP.Get(FBaseURL + APath);
except
on E: EIdHTTPProtocolException do
raise Exception.CreateFmt('Errore HTTP %d: %s', [FHTTP.ResponseCode, E.Message]);
on E: EIdSocketError do
raise Exception.Create('Errore di connessione: ' + E.Message);
end;
end;
end.Un client specifico si limita a ereditare
Per un provider concreto, il client dedicato eredita da TApiClientBase e aggiunge solo i metodi che rispecchiano gli endpoint effettivi, senza duplicare la gestione della connessione:
type
TProviderApiClient = class(TApiClientBase)
public
constructor Create;
function GetDettaglioAnnuncio(const ID: string): string;
end;
constructor TProviderApiClient.Create;
begin
inherited Create('https://api.provider.esempio/v1');
end;
function TProviderApiClient.GetDettaglioAnnuncio(const ID: string): string;
begin
Result := Get('/annunci/' + ID);
end;Un avvertimento prima di usarlo davvero
Questo scheletro presuppone un'API già nota e documentata dal provider: prima di collegarsi a un servizio di terzi bisogna verificarne i termini d'uso e l'esistenza di API pubbliche ufficiali, e seguire esattamente il loro schema di autenticazione (Bearer token è uno dei più comuni, ma non l'unico: alcuni provider usano API key in query string, HMAC delle richieste, o OAuth2 completo). Il token di accesso, una volta ottenuto, va sempre letto da configurazione — mai scritto nel codice sorgente — e il client va esteso con i soli endpoint realmente documentati dal provider scelto.