O novo método HTTP QUERY no Delphi | eSeGeCe Blog

O novo método HTTP QUERY no Delphi

· Recursos

O IETF HTTP Working Group especificou um novo método de requisição HTTP chamado QUERY (draft-ietf-httpbis-safe-method-w-body, a caminho de se tornar um RFC). O QUERY preenche uma lacuna que todo desenvolvedor REST já encontrou: é uma requisição segura e idempotente, como GET, mas que transporta um corpo de requisição, como POST. O sgcWebSockets agora oferece suporte completo ao QUERY, nos clientes HTTP/1.1, HTTP/2 e HTTP/3 e nos componentes de servidor, para Delphi e C++ Builder.

Por que um novo método HTTP?

Até agora havia duas formas de enviar uma consulta a um servidor, e ambas têm problemas. Você pode codificar a consulta na URL de uma requisição GET, mas as URLs têm limites práticos de comprimento, consultas complexas são difíceis de escapar e a URL completa costuma acabar nos logs de proxies e servidores, um péssimo lugar para valores de filtro sensíveis. Ou você pode enviar a consulta no corpo de uma requisição POST, mas o POST não é seguro, não é idempotente e não é armazenável em cache: um cliente HTTP não tem permissão para repeti-lo automaticamente após uma falha de conexão, e os caches não armazenam a resposta.

O QUERY combina o melhor dos dois. O conteúdo da requisição e seu Content-Type definem a consulta, que pode ser tão grande e tão estruturada quanto necessário (dados de formulário, JSON, SQL, JSONPath, o que o servidor aceitar). Ao mesmo tempo, a especificação declara o QUERY seguro e idempotente: ele nunca altera o estado do recurso, pode ser repetido com segurança e suas respostas são armazenáveis em cache, exatamente como uma resposta GET.

Como o QUERY funciona

Uma requisição QUERY se parece com um POST com semântica de GET. Este exemplo, adaptado da especificação, solicita a um recurso de contatos uma projeção filtrada:

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

As regras mais importantes da especificação:

QUERY a partir do cliente HTTP/1.1

O cliente HTTP/1.1 ganha os métodos Query e QueryAsync, que funcionam exatamente como seus equivalentes Post: passe a URL e um stream com o corpo da consulta, defina o Content-Type na requisição e leia o resultado.

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;

Como o QUERY é idempotente, ele também combina bem com as opções de retentativa do cliente: um QUERY que falha com um erro de conexão pode ser repetido automaticamente sem o risco de duplicar uma alteração de estado no servidor. Os mesmos métodos Query estão disponíveis no cliente base de API REST, então integrações de API personalizadas construídas sobre o sgcWebSockets podem adotar o verbo sem nenhum trabalho extra.

QUERY sobre HTTP/2 e HTTP/3

Os clientes HTTP/2 e HTTP/3 expõem o mesmo verbo. Sobre HTTP/2, a requisição é enviada com o pseudo-cabeçalho :method definido como QUERY e o corpo em frames 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;

O cliente HTTP/3 oferece uma sobrecarga com string que usa o content type form-urlencoded como padrão, o que resolve consultas simples com uma única chamada:

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;

Tratando o QUERY no servidor

No lado do servidor, o TsgcWebSocketHTTPServer aceita requisições QUERY sobre HTTP/1.1 e HTTP/2 e as encaminha para o evento OnCommandOther, com o corpo da requisição já lido e disponível em PostStream. Um handler mínimo valida o Content-Type, executa a consulta e retorna o resultado:

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;

O servidor HTTP/3 lê corpos de QUERY da mesma forma, o servidor OpenAPI roteia operações query declaradas em uma especificação OpenAPI e anuncia o método em seus padrões de CORS, e o recurso de encaminhamento HTTP faz proxy de requisições QUERY com seu corpo para um servidor de backend.

Disponibilidade

O suporte ao QUERY está incluído no sgcWebSockets 2026.7 nos clientes HTTP/1.1, HTTP/2 e HTTP/3, no cliente base de API REST, nos componentes de servidor e no servidor OpenAPI, para Delphi 7 a Delphi 13 e C++ Builder. Como o formato de transmissão subjacente é HTTP puro, ele já interopera hoje com qualquer servidor ou cliente que entenda o método. Você pode baixar a versão mais recente na página de download do sgcWebSockets.

Dúvidas, comentários ou ajuda com a migração? Entre em contato, você receberá uma resposta das pessoas que escreveram o código.