De nieuwe HTTP QUERY-methode in Delphi | eSeGeCe Blog

De nieuwe HTTP QUERY-methode in Delphi

· Functies

De IETF HTTP Working Group heeft een nieuwe HTTP-verzoekmethode gespecificeerd met de naam QUERY (draft-ietf-httpbis-safe-method-w-body, op weg om een RFC te worden). QUERY vult een leemte waar elke REST-ontwikkelaar tegenaan is gelopen: het is een veilig en idempotent verzoek, zoals GET, maar het draagt een request body mee, zoals POST. sgcWebSockets ondersteunt QUERY nu van begin tot eind, in de HTTP/1.1-, HTTP/2- en HTTP/3-clients en in de servercomponenten, voor Delphi en C++ Builder.

Waarom een nieuwe HTTP-methode?

Tot nu toe waren er twee manieren om een query naar een server te sturen, en beide hebben problemen. U kunt de query coderen in de URL van een GET-verzoek, maar URL's hebben praktische lengtelimieten, complexe query's zijn lastig te escapen, en de volledige URL belandt vaak in proxy- en serverlogs, wat een slechte plek is voor gevoelige filterwaarden. Of u kunt de query verzenden in de body van een POST-verzoek, maar POST is niet veilig, niet idempotent en niet cachebaar: een HTTP-client mag het verzoek na een verbindingsfout niet automatisch opnieuw proberen, en caches slaan het antwoord niet op.

QUERY combineert het beste van beide. De inhoud van het verzoek en zijn Content-Type definiëren de query, dus deze kan zo groot en zo gestructureerd zijn als nodig (formulierdata, JSON, SQL, JSONPath, wat de server maar accepteert). Tegelijkertijd verklaart de specificatie QUERY veilig en idempotent: het verandert nooit de status van de resource, het kan veilig opnieuw geprobeerd of herhaald worden, en de antwoorden zijn cachebaar, net als een GET-antwoord.

Hoe QUERY werkt

Een QUERY-verzoek ziet eruit als een POST met GET-semantiek. Dit voorbeeld, overgenomen uit de specificatie, vraagt een contactenresource om een gefilterde projectie:

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" }
]

De belangrijkste regels uit de specificatie:

QUERY vanuit de HTTP/1.1-client

De HTTP/1.1-client krijgt de methoden Query en QueryAsync, die precies zo werken als hun Post-tegenhangers: geef de URL en een stream met de query-body door, stel de Content-Type in het verzoek in en lees het resultaat.

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;

Omdat QUERY idempotent is, werkt het ook goed samen met de retry-opties van de client: een QUERY die mislukt door een verbindingsfout kan automatisch opnieuw worden geprobeerd zonder het risico dat een statuswijziging op de server wordt gedupliceerd. Dezelfde Query-methoden zijn beschikbaar op de REST API-basisclient, zodat aangepaste API-integraties die op sgcWebSockets zijn gebouwd het verb zonder extra werk kunnen overnemen.

QUERY via HTTP/2 en HTTP/3

De HTTP/2- en HTTP/3-clients bieden hetzelfde verb. Via HTTP/2 wordt het verzoek verzonden met de :method pseudo-header ingesteld op QUERY en de body in DATA-frames:

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;

De HTTP/3-client biedt een string-overload met het form-urlencoded contenttype als standaard, waardoor eenvoudige query's tot één enkele aanroep beperkt blijven:

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;

QUERY afhandelen op de server

Aan de serverzijde accepteert TsgcWebSocketHTTPServer QUERY-verzoeken via HTTP/1.1 en HTTP/2 en stuurt ze door naar het OnCommandOther-event, waarbij de request body al is gelezen en beschikbaar is in PostStream. Een minimale handler valideert de Content-Type, voert de query uit en retourneert het resultaat:

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;

De HTTP/3-server leest QUERY-bodies op dezelfde manier, de OpenAPI-server routeert query-operaties die in een OpenAPI-spec zijn gedeclareerd en adverteert de methode in zijn CORS-standaardinstellingen, en de HTTP-forwardingfunctie stuurt QUERY-verzoeken met hun body als proxy door naar een backendserver.

Beschikbaarheid

QUERY-ondersteuning is opgenomen in sgcWebSockets 2026.7 in de HTTP/1.1-, HTTP/2- en HTTP/3-clients, de REST API-basisclient, de servercomponenten en de OpenAPI-server, voor Delphi 7 tot en met Delphi 13 en C++ Builder. Omdat het onderliggende wire-formaat gewoon HTTP is, werkt het vandaag al samen met elke server of client die de methode begrijpt. U kunt de nieuwste versie downloaden van de sgcWebSockets-downloadpagina.

Vragen, feedback of hulp bij migratie? Neem contact op, u krijgt antwoord van de mensen die de code hebben geschreven.