Il nuovo metodo HTTP QUERY in Delphi | eSeGeCe Blog

Il nuovo metodo HTTP QUERY in Delphi

· Funzionalità

Il gruppo di lavoro HTTP dell'IETF ha specificato un nuovo metodo di richiesta HTTP chiamato QUERY (draft-ietf-httpbis-safe-method-w-body, in procinto di diventare un RFC). QUERY colma una lacuna in cui ogni sviluppatore REST si è imbattuto: è una richiesta sicura e idempotente, come GET, ma trasporta un corpo di richiesta, come POST. sgcWebSockets ora supporta QUERY end to end, nei client HTTP/1.1, HTTP/2 e HTTP/3 e nei componenti server, per Delphi e C++ Builder.

Perché un nuovo metodo HTTP?

Fino ad ora c'erano due modi per inviare una query a un server, ed entrambi presentano problemi. Puoi codificare la query nell'URL di una richiesta GET, ma gli URL hanno limiti pratici di lunghezza, le query complesse sono difficili da gestire con l'escape e l'URL completo tende a finire nei log di proxy e server, che è un pessimo posto per valori di filtro sensibili. Oppure puoi inviare la query nel corpo di una richiesta POST, ma POST non è sicuro, non è idempotente e non è memorizzabile nella cache: un client HTTP non può ritentarlo automaticamente dopo un errore di connessione, e le cache non memorizzeranno la risposta.

QUERY combina il meglio di entrambi. Il contenuto della richiesta e il suo Content-Type definiscono la query, che può quindi essere grande e strutturata quanto necessario (dati di form, JSON, SQL, JSONPath, qualsiasi cosa il server accetti). Allo stesso tempo la specifica dichiara QUERY sicuro e idempotente: non modifica mai lo stato della risorsa, può essere ritentato o ripetuto in sicurezza e le sue risposte sono memorizzabili nella cache esattamente come una risposta GET.

Come funziona QUERY

Una richiesta QUERY assomiglia a una POST con la semantica di GET. Questo esempio, adattato dalla specifica, chiede a una risorsa di contatti una proiezione filtrata:

QUERY /contacts HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded
Accept: application/json

select=surname,givenname,email&limit=10

HTTP/1.1 200 OK
Content-Type: application/json

[
  { "surname": "Smith", "givenname": "John", "email": "smith@example.org" },
  { "surname": "Jones", "givenname": "Sally", "email": "sally.jones@example.com" }
]

Le regole più importanti della specifica:

QUERY dal client HTTP/1.1

Il client HTTP/1.1 acquisisce i metodi Query e QueryAsync, che funzionano esattamente come le loro controparti Post: passa l'URL e uno stream con il corpo della query, imposta il Content-Type nella richiesta e leggi il risultato.

uses
  Classes, sgcHTTP;

var
  oClient: TsgcHTTP1Client;
  oQuery: TStringStream;
  vResult: string;
begin
  oClient := TsgcHTTP1Client.Create(nil);
  try
    oQuery := TStringStream.Create('select=surname,givenname,email&limit=10');
    try
      oClient.Request.ContentType := 'application/x-www-form-urlencoded';
      oClient.Request.Accept := 'application/json';
      vResult := oClient.Query('https://api.example.org/contacts', oQuery);
    finally
      oQuery.Free;
    end;
  finally
    oClient.Free;
  end;
end;

Poiché QUERY è idempotente, si integra bene anche con le opzioni di retry del client: una QUERY che fallisce con un errore di connessione può essere ritentata automaticamente senza il rischio di duplicare una modifica di stato sul server. Gli stessi metodi Query sono disponibili nel client base delle REST API, quindi le integrazioni API personalizzate costruite su sgcWebSockets possono adottare il verbo senza alcun lavoro aggiuntivo.

QUERY su HTTP/2 e HTTP/3

I client HTTP/2 e HTTP/3 espongono lo stesso verbo. Su HTTP/2 la richiesta viene inviata con lo pseudo-header :method impostato a QUERY e il corpo in frame DATA:

uses
  Classes, sgcHTTP;

var
  oClient: TsgcHTTP2Client;
  oQuery: TStringStream;
  vResult: string;
begin
  oClient := TsgcHTTP2Client.Create(nil);
  try
    oQuery := TStringStream.Create('select=surname,givenname,email&limit=10');
    try
      vResult := oClient.Query('https://api.example.org/contacts', oQuery);
    finally
      oQuery.Free;
    end;
  finally
    oClient.Free;
  end;
end;

Il client HTTP/3 offre un overload con stringa che usa come predefinito il content type form-urlencoded, il che riduce le query semplici a una singola chiamata:

uses
  sgcHTTP;

var
  oClient: TsgcHTTP3Client;
  vResult: string;
begin
  oClient := TsgcHTTP3Client.Create(nil);
  try
    vResult := oClient.Query('https://api.example.org/contacts',
      'select=surname,givenname,email&limit=10');
  finally
    oClient.Free;
  end;
end;

Gestire QUERY sul server

Sul lato server, TsgcWebSocketHTTPServer accetta richieste QUERY su HTTP/1.1 e HTTP/2 e le inoltra all'evento OnCommandOther, con il corpo della richiesta già letto e disponibile in PostStream. Un handler minimale valida il Content-Type, esegue la query e restituisce il risultato:

procedure TForm1.ServerCommandOther(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  vQuery: string;
begin
  if SameText(ARequestInfo.Command, 'QUERY') then
  begin
    // the specification requires a Content-Type on every QUERY request
    if (ARequestInfo.ContentType = '') or
      not Assigned(ARequestInfo.PostStream) then
    begin
      AResponseInfo.ResponseNo := 400;
      Exit;
    end;

    ARequestInfo.PostStream.Position := 0;
    vQuery := ReadStringFromStream(ARequestInfo.PostStream);

    AResponseInfo.ResponseNo := 200;
    AResponseInfo.ContentType := 'application/json';
    AResponseInfo.ContentText := RunContactsQuery(vQuery);
  end;
end;

Il server HTTP/3 legge i corpi delle richieste QUERY allo stesso modo, il server OpenAPI instrada le operazioni query dichiarate in una specifica OpenAPI e annuncia il metodo nelle sue impostazioni CORS predefinite, e la funzionalità di forwarding HTTP inoltra le richieste QUERY con il loro corpo a un server backend.

Disponibilità

Il supporto per QUERY è incluso in sgcWebSockets 2026.7 nei client HTTP/1.1, HTTP/2 e HTTP/3, nel client base delle REST API, nei componenti server e nel server OpenAPI, per Delphi da 7 a 13 e C++ Builder. Poiché il formato di trasmissione sottostante è semplice HTTP, interopera già oggi con qualsiasi server o client che comprenda il metodo. Puoi scaricare l'ultima versione dalla pagina di download di sgcWebSockets.

Domande, feedback o aiuto con la migrazione? Contattaci, riceverai una risposta dalle persone che hanno scritto il codice.