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:
- Elk QUERY-verzoek moet een
Content-Typebevatten die overeenkomt met de body. Een server moet het verzoek weigeren met een 4xx-status (400, 415 of 422) wanneer de header ontbreekt of niet consistent is met de inhoud. - Antwoorden zijn cachebaar. Een cache die ze opslaat, moet de request body opnemen in de cachesleutel, zodat twee verschillende query's nooit botsen.
- Redirects behouden de methode: na een 301, 302, 307 of 308 herhaalt de client de QUERY op de nieuwe URI. De oude uitzondering die een geredirecte POST herschrijft naar een GET geldt niet voor QUERY. Een 303 See Other is de expliciete manier voor een server om te zeggen "haal het resultaat op met een GET van deze Location".
- Een server kan met de nieuwe
Accept-Query-responseheader aangeven welke queryformaten een resource begrijpt, en QUERY verschijnt in deAllow-header van een OPTIONS-antwoord wanneer de methode wordt ondersteund. - In browsers is QUERY geen CORS-safelisted methode, dus cross-origin-aanroepen veroorzaken een preflight. De CORS-standaardinstellingen van de sgcWebSockets OpenAPI-server nemen QUERY al op in
Access-Control-Allow-Methods.
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.
