Nowa metoda HTTP QUERY w Delphi | eSeGeCe Blog

Nowa metoda HTTP QUERY w Delphi

· Funkcje

Grupa robocza IETF HTTP opracowała specyfikację nowej metody żądania HTTP o nazwie QUERY (draft-ietf-httpbis-safe-method-w-body, na drodze do stania się RFC). QUERY wypełnia lukę, na którą natknął się każdy programista REST: jest to żądanie bezpieczne i idempotentne, jak GET, ale przenosi treść żądania, jak POST. sgcWebSockets obsługuje teraz QUERY od początku do końca, w klientach HTTP/1.1, HTTP/2 i HTTP/3 oraz w komponentach serwerowych, dla Delphi i C++ Builder.

Dlaczego nowa metoda HTTP?

Do tej pory istniały dwa sposoby wysłania zapytania do serwera i oba mają swoje problemy. Można zakodować zapytanie w adresie URL żądania GET, ale adresy URL mają praktyczne limity długości, złożone zapytania są uciążliwe w escapowaniu, a pełny adres URL zwykle trafia do logów proxy i serwera, co jest złym miejscem dla wrażliwych wartości filtrów. Można też wysłać zapytanie w treści żądania POST, ale POST nie jest bezpieczny, nie jest idempotentny i nie podlega buforowaniu: klient HTTP nie może automatycznie ponowić go po awarii połączenia, a pamięci podręczne nie zapiszą odpowiedzi.

QUERY łączy najlepsze cechy obu podejść. Treść żądania i jej Content-Type definiują zapytanie, więc może być tak duże i tak ustrukturyzowane, jak potrzeba (dane formularza, JSON, SQL, JSONPath, cokolwiek akceptuje serwer). Jednocześnie specyfikacja deklaruje QUERY jako bezpieczne i idempotentne: nigdy nie zmienia stanu zasobu, może być bezpiecznie ponawiane lub powtarzane, a jego odpowiedzi podlegają buforowaniu dokładnie tak, jak odpowiedź GET.

Jak działa QUERY

Żądanie QUERY wygląda jak POST z semantyką GET. Ten przykład, zaadaptowany ze specyfikacji, prosi zasób kontaktów o przefiltrowaną projekcję:

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

Najważniejsze reguły ze specyfikacji:

QUERY z klienta HTTP/1.1

Klient HTTP/1.1 zyskuje metody Query i QueryAsync, które działają dokładnie tak, jak ich odpowiedniki Post: przekaż adres URL i strumień z treścią zapytania, ustaw Content-Type w żądaniu i odczytaj wynik.

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;

Ponieważ QUERY jest idempotentne, dobrze współpracuje również z opcjami ponawiania klienta: żądanie QUERY, które nie powiodło się z powodu błędu połączenia, może zostać automatycznie ponowione bez ryzyka zduplikowania zmiany stanu na serwerze. Te same metody Query są dostępne w bazowym kliencie REST API, więc niestandardowe integracje API zbudowane na sgcWebSockets mogą przyjąć ten czasownik bez dodatkowej pracy.

QUERY przez HTTP/2 i HTTP/3

Klienci HTTP/2 i HTTP/3 udostępniają ten sam czasownik. Przez HTTP/2 żądanie jest wysyłane z pseudonagłówkiem :method ustawionym na QUERY i treścią w ramkach 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;

Klient HTTP/3 oferuje przeciążenie przyjmujące łańcuch znaków, z typem treści form-urlencoded jako domyślnym, co sprowadza proste zapytania do jednego wywołania:

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;

Obsługa QUERY na serwerze

Po stronie serwera TsgcWebSocketHTTPServer akceptuje żądania QUERY przez HTTP/1.1 i HTTP/2 i przekazuje je do zdarzenia OnCommandOther, z treścią żądania już odczytaną i dostępną w PostStream. Minimalna procedura obsługi weryfikuje Content-Type, uruchamia zapytanie i zwraca wynik:

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;

Serwer HTTP/3 odczytuje treści QUERY w ten sam sposób, serwer OpenAPI kieruje operacje query zadeklarowane w specyfikacji OpenAPI i ogłasza tę metodę w swoich domyślnych ustawieniach CORS, a funkcja przekazywania HTTP przekazuje żądania QUERY wraz z ich treścią do serwera zaplecza.

Dostępność

Obsługa QUERY jest zawarta w sgcWebSockets 2026.7 w klientach HTTP/1.1, HTTP/2 i HTTP/3, bazowym kliencie REST API, komponentach serwerowych i serwerze OpenAPI, dla Delphi 7 do Delphi 13 oraz C++ Builder. Ponieważ format przesyłany po sieci to zwykłe HTTP, metoda już dziś współpracuje z każdym serwerem lub klientem, który ją rozumie. Najnowszą wersję można pobrać ze strony pobierania sgcWebSockets.

Pytania, uwagi lub pomoc w migracji? Skontaktuj się z nami, odpowiedzą osoby, które napisały ten kod.