La nouvelle méthode HTTP QUERY en Delphi | Blog eSeGeCe

La nouvelle méthode HTTP QUERY en Delphi

· Fonctionnalités

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 :

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.