Le groupe de travail HTTP de l'IETF a spécifié une nouvelle méthode de requête HTTP appelée QUERY (draft-ietf-httpbis-safe-method-w-body, en passe de devenir une RFC). QUERY comble une lacune que tout développeur REST a déjà rencontrée : c'est une requête sûre et idempotente, comme GET, mais qui transporte un corps de requête, comme POST. sgcWebSockets prend désormais en charge QUERY de bout en bout, dans les clients HTTP/1.1, HTTP/2 et HTTP/3 et dans les composants serveur, pour Delphi et C++ Builder.
Pourquoi une nouvelle méthode HTTP ?
Jusqu'à présent, il existait deux façons d'envoyer une requête de recherche à un serveur, et les deux posent des problèmes. Vous pouvez encoder la requête dans l'URL d'une requête GET, mais les URL ont des limites de longueur pratiques, les requêtes complexes sont pénibles à échapper, et l'URL complète finit souvent dans les journaux des proxys et des serveurs, ce qui est un mauvais endroit pour des valeurs de filtre sensibles. Ou vous pouvez envoyer la requête dans le corps d'une requête POST, mais POST n'est ni sûre, ni idempotente, ni mise en cache : un client HTTP n'est pas autorisé à la relancer automatiquement après un échec de connexion, et les caches ne stockeront pas la réponse.
QUERY combine le meilleur des deux. Le contenu de la requête et son Content-Type définissent la requête, qui peut donc être aussi volumineuse et structurée que nécessaire (données de formulaire, JSON, SQL, JSONPath, tout ce que le serveur accepte). En même temps, la spécification déclare QUERY sûre et idempotente : elle ne modifie jamais l'état de la ressource, elle peut être relancée ou répétée sans risque, et ses réponses peuvent être mises en cache exactement comme une réponse GET.
Comment fonctionne QUERY
Une requête QUERY ressemble à un POST avec la sémantique d'un GET. Cet exemple, adapté de la spécification, demande à une ressource de contacts une projection filtrée :
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" }
]
Les règles les plus importantes de la spécification :
- Chaque requête QUERY doit inclure un
Content-Typecorrespondant à son corps. Un serveur doit rejeter la requête avec un statut 4xx (400, 415 ou 422) lorsque l'en-tête est absent ou incohérent avec le contenu. - Les réponses peuvent être mises en cache. Un cache qui les stocke doit inclure le corps de la requête dans la clé de cache, afin que deux requêtes différentes n'entrent jamais en collision.
- Les redirections préservent la méthode : après un 301, 302, 307 ou 308, le client répète la requête QUERY vers le nouvel URI. L'ancienne exception qui réécrit un POST redirigé en GET ne s'applique pas à QUERY. Un 303 See Other est le moyen explicite pour un serveur de dire « récupérez le résultat avec un GET depuis cette Location ».
- Un serveur peut annoncer les formats de requête compris par une ressource grâce au nouvel en-tête de réponse
Accept-Query, et QUERY apparaît dans l'en-têteAllowd'une réponse OPTIONS lorsqu'il est pris en charge. - Dans les navigateurs, QUERY n'est pas une méthode de la liste sûre CORS, donc les appels cross-origin déclenchent une requête préliminaire (preflight). Les valeurs CORS par défaut du serveur OpenAPI de sgcWebSockets incluent déjà QUERY dans
Access-Control-Allow-Methods.
QUERY depuis le client HTTP/1.1
Le client HTTP/1.1 gagne des méthodes Query et QueryAsync qui fonctionnent exactement comme leurs équivalents Post : passez l'URL et un flux contenant le corps de la requête, définissez le Content-Type dans la requête et lisez le résultat.
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;
Comme QUERY est idempotente, elle s'accorde aussi très bien avec les options de nouvelle tentative du client : une requête QUERY qui échoue avec une erreur de connexion peut être relancée automatiquement sans risque de dupliquer un changement d'état sur le serveur. Les mêmes méthodes Query sont disponibles sur le client de base API REST, de sorte que les intégrations d'API personnalisées construites sur sgcWebSockets peuvent adopter ce verbe sans travail supplémentaire.
QUERY sur HTTP/2 et HTTP/3
Les clients HTTP/2 et HTTP/3 exposent le même verbe. Sur HTTP/2, la requête est envoyée avec le pseudo-en-tête :method défini à QUERY et le corps dans des trames 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;
Le client HTTP/3 offre une surcharge de type chaîne avec le type de contenu form-urlencoded par défaut, ce qui permet de réaliser les requêtes simples en un seul appel :
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;
Traiter QUERY côté serveur
Côté serveur, TsgcWebSocketHTTPServer accepte les requêtes QUERY sur HTTP/1.1 et HTTP/2 et les transmet à l'événement OnCommandOther, avec le corps de la requête déjà lu et disponible dans PostStream. Un gestionnaire minimal valide le Content-Type, exécute la requête et renvoie le résultat :
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;
Le serveur HTTP/3 lit les corps QUERY de la même manière, le serveur OpenAPI route les opérations query déclarées dans une spécification OpenAPI et annonce la méthode dans ses valeurs CORS par défaut, et la fonctionnalité de transfert HTTP relaie les requêtes QUERY avec leur corps vers un serveur backend.
Disponibilité
La prise en charge de QUERY est incluse dans sgcWebSockets 2026.7 pour les clients HTTP/1.1, HTTP/2 et HTTP/3, le client de base API REST, les composants serveur et le serveur OpenAPI, de Delphi 7 à Delphi 13 et pour C++ Builder. Comme le format sous-jacent est du HTTP standard, elle interopère dès aujourd'hui avec tout serveur ou client qui comprend la méthode. Vous pouvez télécharger la dernière version depuis la page de téléchargement de sgcWebSockets.
Des questions, des retours ou besoin d'aide pour la migration ? Contactez-nous, vous recevrez une réponse des personnes qui ont écrit le code.
