RFC 10008: o novo método HTTP QUERY
(rfc-editor.org)- A RFC 10008 define o método HTTP QUERY, no qual o recurso de destino processa de forma segura e idempotente uma consulta contida no corpo da requisição e retorna o resultado
- O QUERY combina o caráter safe/idempotent do GET com o envio de corpo do POST, reduzindo problemas com URIs longas, custo de codificação em URI, exposição em logs e a necessidade de tratar cada combinação de consulta como um recurso separado
- O servidor só pode processar uma requisição QUERY quando o Content-Type e o corpo forem consistentes; tipos não suportados, incompatibilidade entre tipo e corpo e consultas impossíveis de processar podem ser distinguidos com diferentes respostas 4xx
- Uma resposta bem-sucedida pode indicar, com Content-Location, o recurso do resultado da consulta e, com Location, um recurso equivalente para repetir a mesma consulta
- Respostas QUERY podem ser armazenadas em cache, mas a chave de cache deve incluir também o corpo e os metadados; em ambientes CORS, como não é um safelisted method, é necessário preflight
O padrão de consulta HTTP que o QUERY tenta resolver
- A RFC 10008 é um documento Internet Standards Track que define o método de requisição HTTP QUERY
- O QUERY solicita que o recurso de destino processe o corpo da requisição e responda com seu resultado
- Assim como o POST, ele usa corpo, mas é definido como safe e idempotent, permitindo tentativas automáticas novamente ou reinício
- Em consultas GET tradicionais, é comum colocar a entrada na URI
GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
- Colocar dados de consulta na URI traz mais restrições à medida que os dados crescem
- Como a requisição passa por vários sistemas independentes, é difícil saber antecipadamente o limite real de tamanho da URI
- O HTTP recomenda que remetente e destinatário suportem no mínimo 8000 octets, mas isso não garante todos os sistemas no caminho
- Alguns dados têm alto custo para serem codificados como URI válida
- A URI da requisição tem mais chance de ir para logs ou bookmarks do que o corpo da requisição
- Ao codificar a consulta diretamente na URI, cada combinação possível de entrada passa a ser tratada como um recurso distinto
Um método que esclarece o significado entre GET e POST
- Muitas implementações enviam consultas no corpo de um POST em vez de usar GET
POST /feedContent-Type: application/x-www-form-urlencoded- corpo:
q=foo&limit=10&sort=-published
- Sem conhecimento específico do recurso e do servidor, esse padrão não deixa claro se a consulta é segura e idempotente
- O QUERY envia a mesma entrada no corpo da requisição, mas o próprio método já é seguro e idempotente
QUERY /feedContent-Type: application/x-www-form-urlencoded- corpo:
q=foo&limit=10&sort=-published
- Esse significado explícito facilita aplicar recursos HTTP como cache e tentativas automáticas novamente
- O servidor pode atribuir uma URI à própria consulta ou ao resultado de uma consulta específica para uso posterior com GET
Regras centrais do método QUERY
- O QUERY é usado para iniciar consultas no lado do servidor
- O GET solicita a representação do recurso identificado pela URI de destino, enquanto o QUERY solicita a execução de uma operação de consulta dentro do escopo do recurso de destino
- O corpo da requisição e seu tipo de mídia definem a consulta, e o origin server determina o escopo da operação com base no recurso de destino
- O servidor deve falhar a requisição se o campo de requisição Content-Type estiver ausente ou não corresponder ao corpo da requisição
- A query part da URI de destino participa da identificação do recurso consultado, como em qualquer método HTTP
- Se essa query part afeta diretamente o resultado, e de que forma, é comportamento específico do recurso e está fora do escopo desta especificação
- O QUERY é seguro do ponto de vista do recurso de destino
- O cliente não solicita nem espera alteração de estado no recurso de destino
- Não é proibido que o servidor crie recursos HTTP para consulta de informações adicionais
- O QUERY é idempotente, então pode ser repetido ou reenviado quando necessário após falha de conexão
- Uma resposta
200 OKindica que a consulta foi processada com sucesso e que o resultado está incluído no corpo da resposta
Tipos de mídia, negociação e tratamento de erros
- O significado de uma requisição QUERY depende do corpo da requisição e de metadados associados, como o tipo de mídia
- Requisições em que corpo e metadados não sejam consistentes normalmente devem ser rejeitadas com 4xx Client Error
- O tratamento do erro varia conforme o ponto em que a requisição está incorreta
- Se não houver informação de tipo de mídia, a requisição é inválida por definição e deve falhar com um código 4xx como
400 - Se o tipo de mídia foi especificado, mas o recurso não o suporta,
415 Unsupported Media Typeé apropriado - Mesmo que o tipo de mídia seja conhecido em princípio, se não houver semântica QUERY para o recurso de destino, também cabe
415 - Se o tipo de mídia não corresponder ao corpo real da requisição, o servidor pode retornar
400 Bad Request - O servidor não pode fazer content sniffing do corpo para inferir o tipo de mídia e sobrescrever um valor ausente ou incorreto
- Se tipo e corpo estiverem corretos, mas o conteúdo real da consulta não puder ser processado, pode-se usar
422 Unprocessable Content - Um exemplo de
422é uma consulta SQL sintaticamente válida que aponta para uma tabela inexistente - Se o recurso não suportar o tipo de mídia de resposta solicitado pelo cliente em
Accept,406 Not Acceptableé apropriado
- Se não houver informação de tipo de mídia, a requisição é inválida por definição e deve falhar com um código 4xx como
- O campo de resposta
Accept-Querypode informar ao cliente quais tipos de mídia de consulta são suportados
recurso equivalente, Content-Location e Location
- Um recurso equivalente é um recurso que representa uma requisição QUERY específica e seu alvo, e que responde a requisições GET
- O recurso equivalente considera tanto o corpo da requisição quanto os metadados
- Isso inclui metadados de representação, como o tipo de mídia do corpo
- O servidor pode atribuir uma URI ao recurso equivalente, mas isso não é obrigatório
- Uma resposta bem-sucedida pode incluir, no cabeçalho Content-Location, um identificador do recurso correspondente ao resultado da consulta
- O cliente pode enviar um GET para a URI indicada e recuperar o resultado da operação de consulta recém-executada
- Esse recurso pode ser temporário
- Uma resposta bem-sucedida pode incluir, no cabeçalho Location, a URI do recurso equivalente da requisição QUERY
- O cliente pode repetir a mesma operação de consulta enviando um GET para a URI indicada, sem precisar reenviar o corpo da consulta
- Essa URI também pode ser temporária
- Se uma requisição posterior falhar, o cliente pode tentar novamente usando o alvo QUERY original e o corpo enviado anteriormente
Redirecionamento e requisições condicionais
- O servidor pode optar por uma resposta indireta que redirecione o user agent para outra URI em resposta a uma requisição QUERY
301 Moved Permanentlye308 Permanent Redirectindicam que o recurso de destino foi movido permanentemente para outra URI apontada por Location302 Founde307 Temporary Redirectindicam uma movimentação temporária do recurso de destino- Nos quatro casos, o servidor sugere que a requisição original pode ser executada enviando uma requisição QUERY semelhante para a nova URI de destino
- A exceção de redirecionar POST para GET após
301ou302não se aplica a requisições QUERY - Um
303 See Otherpara QUERY indica que a consulta original pode ser executada como uma requisição comum de recuperação ao URI apontado por Location- Em HTTP, isso significa enviar um GET para a nova URI de destino
- Em QUERY condicional, a selected representation é a mesma que em um GET para o recurso equivalente dessa requisição QUERY
- O cliente pode solicitar que o resultado da consulta seja retornado apenas se as condições especificadas pelos cabeçalhos condicionais forem atendidas
Cache e requisições Range
- Respostas ao método QUERY podem ser armazenadas em cache, e o cache pode ser usado para satisfazer requisições QUERY futuras
- A chave de cache de uma requisição QUERY deve incluir o corpo da requisição e os metadados relacionados
- O cache pode remover diferenças semanticamente irrelevantes ao gerar a chave de cache
- remoção de content encoding
- normalização segundo convenções de formato indicadas por sufixos de subtipo de mídia, como
+json - normalização conforme o significado do corpo indicado por
Content-Type
- Essas transformações servem apenas para gerar a chave de cache e não modificam a requisição em si
- O cliente pode indicar, com a diretiva de cache
no-transform, que não deseja essas transformações, mas essa diretiva é advisory - O cache de respostas QUERY é inerentemente mais complexo do que o de GET
- Para determinar a chave de cache, é preciso ler todo o corpo da requisição
- Se a resposta QUERY fornecer uma URI de recurso equivalente em Location, o cliente pode depois migrar para GET e simplificar o processamento
- O significado de Range Request em QUERY é o mesmo que em GET
- A única range unit definida até o momento, Byte Range Request, tem pouco valor para resultados de QUERY
- Em muitos casos, o próprio formato da consulta já oferece limitação de resultado ou paginação, como
FETCH FIRST ... ROWS ONLYem SQL, e espera-se o uso desses recursos embutidos
Cabeçalho de resposta Accept-Query
- O cabeçalho de resposta Accept-Query informa diretamente que um recurso suporta o método QUERY e identifica quais tipos de mídia de formato de consulta podem ser usados
- O Accept-Query é uma lista de media ranges usando a sintaxe de Structured Fields
- Um media range é expresso como um campo de cabeçalho estruturado do tipo List, com Token ou String contendo um valor de media range sem parâmetros
- Parâmetros de tipo de mídia são mapeados para Structured Field Parameters
- A escolha entre String e Token não é semanticamente importante
- O receptor pode converter Token em String, mas não deve tratar de forma diferente conforme o tipo recebido
- Tipos de mídia não se mapeiam exatamente para Token e, quando for necessário permitir números no início, deve-se usar o formato String
- Os únicos wildcards suportados são
*/*exxxx/* - A ordem dos tipos listados no valor do campo não é importante
- O valor de Accept-Query se aplica a todas as URIs do servidor que compartilhem o mesmo path, e o query component é ignorado
- Se a mesma requisição de recurso retornar valores diferentes de Accept-Query, usa-se o valor fresh mais recente recebido
- O exemplo é o seguinte
Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"
- O Accept-Query se parece com
Accept, mas, por ser um Structured Field, deve seguir as regras de processamento de Structured Fields da RFC 9651
Considerações de segurança e CORS
- O QUERY segue as considerações gerais de segurança de todos os métodos HTTP definidas na RFC 9110
- O QUERY pode ser usado como alternativa a colocar informações da requisição no query component da URI
- Como URIs têm mais chance de ir para logs ou serem processadas por intermediários do que corpos de requisição, o QUERY pode ser preferível ao GET em consultas com informações sensíveis
- Quando o servidor cria um recurso temporário para representar o resultado de uma QUERY e lhe atribui uma URI, não deve incluir nessa URI partes sensíveis se o corpo original da requisição contiver informações que não deveriam ser registradas em logs
- Se um cache normalizar incorretamente o corpo de uma QUERY, ou de forma muito diferente do modo como o recurso o processa, pode retornar uma resposta errada por falso positivo
- Requisições QUERY de user agents que implementam CORS exigem uma requisição preflight
- O QUERY não faz parte do conjunto de CORS-safelisted methods
Registro na IANA e escolha do nome do método
- A IANA adicionou o método QUERY ao HTTP Method Registry
- Method Name:
QUERY - Safe:
yes - Idempotent:
yes - Specification: RFC 10008 Section 2
- Method Name:
- A IANA também adicionou o campo Accept-Query ao HTTP Field Name Registry
- Field Name:
Accept-Query - Status:
permanent - Structured Type:
List
- Field Name:
- O HTTP Method Registry já continha
PROPFIND,REPORTeSEARCH, que também têm propriedades safe e idempotent - Em estágios iniciais foi usado
SEARCH, mas o nome final do método passou a ser QUERY - O QUERY foi escolhido pelos seguintes motivos
- As alternativas usavam o tipo de mídia genérico
application/xmlno corpo da requisição, e o significado da requisição dependia inteiramente do corpo - Todas as alternativas vinham de atividades relacionadas ao WebDAV
QUERYcaptura bem a relação com o query component da URI
- As alternativas usavam o tipo de mídia genérico
1 comentários
Comentários do Hacker News
Teria sido mais convincente se houvesse um exemplo de motivação forte, mas usaram exemplos que podem ser representados com GET com facilidade demais, o que acabou até distraindo
Mesmo imaginando um QUERY com uma estrutura grande de filtro em JSON ou entrada de imagem no corpo da requisição, parece muito estranho que o corpo da requisição vire parte da chave de cache. Isso cria chaves de cache infinitas controladas pelo usuário, e as estratégias normais de cache acabam basicamente se resumindo a comparar ou hashear o corpo da requisição bit a bit, o que torna muito fácil invalidar o cache em cenários maliciosos
Se eu estivesse criando um serviço que exige filtragem complexa ou entradas complexas como imagens, é bem provável que o cache ficasse longe da camada HTTP. Por exemplo, talvez a chave fosse baseada em colunas individuais de dados de um join, ou em embeddings indexados por hash perceptual da imagem decodificada, e não na representação exata em bits transmitida no wire
Não entendo por que tentar capturar isso à força de forma genérica. Eu preferiria muito mais expressar a semântica de cache no POST com um novo cabeçalho tipo
"Vary: request-body". Seria totalmente retrocompatível e poderia ser ignorado fora do 0,1% dos casos de CDN em que esse comportamento talvez seja útilNão consigo pensar em nenhum ataque relacionado a cache que não se aplique igualmente a parâmetros de query. Se alguém quiser estourar o cache, criar um parâmetro de query único de 30 caracteres é tão fácil quanto criar um corpo de requisição de 30 MB
Na prática, sistemas voltados para a internet pública usariam um hash criptográfico como chave de cache para que ela tenha sempre o mesmo tamanho. Chaves de cache já incluem URLs potencialmente muito longas e conjuntos arbitrários de valores de cabeçalho
GET com parâmetros de query já é opaco e já facilita invalidação de cache
Mas acho que QUERY antes de POST melhoraria isso, porque deixariam de ser o mesmo tipo de requisição com um flag de segurança e passariam a ser tipos de requisição diferentes
Fico me perguntando se formulários HTML vão adicionar suporte a QUERY
QUERY deveria ser idempotente, então poderia evitar aquele aviso irritante de reenvio ao atualizar a página de resultado de um envio de formulário POST
Se adicionarem
method=QUERY, isso só cria mais uma variante dessa estranhezaPara quem ainda quer fingir que está no século passado: https://www.rfc-editor.org/rfc/rfc10008.txt
“A opção de anexar um corpo a uma requisição GET foi profundamente considerada pelo grupo de trabalho da IETF, mas no fim decidiu-se criar o novo método QUERY. A decisão de criar um método separado foi motivada por problemas históricos de interoperabilidade e pela adesão estrita à definição arquitetural central do HTTP”
Só que eu já venho enviando corpo de requisição com método GET há anos
fetch, por exemplo, é assimhttps://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U...
“GET requests cannot include a body”
Também pode causar problemas estranhos por causa de cache transparente
É impressionante que já chegamos a números RFC de 5 dígitos
Alguém fez uma aposta ambígua sobre quando o RFC 10000 seria publicado, mas a numeração pulou direto de 9998 para 10008. Ninguém ganhou
https://manifold.markets/CollectedOverSpread/when-will-rfc-1...
Se para consultar resultados de busca em HTTP você usar o método QUERY e não adicionar parâmetros de query, então o nome fica confuso. O termo
queryjá é usado para se referir a requisições HTTP de forma geralSó o título do RFC já me confundiu
queryé usado para se referir a requisições HTTP em geral? Coloquialmente até se chama requisição GET de consulta, mas POST, PUT e DELETE nunca são chamados assimEdit: ah, entendi, declararam QUERY como método “seguro”, sem efeitos colaterais, por causa da possibilidade de cache. Eu tinha me confundido
Se isso realmente acabar substituindo na prática as requisições GET com query string na natureza, eu realmente espero que os favoritos do navegador passem a suportar preservação dos parâmetros da requisição
Isso foge do escopo deste RFC, mas gosto que uma extensão simples disso poderia fazer o
EventSourcedo JS funcionar também para consultas de IA em streamingComo a requisição precisa de corpo, todo mundo usa POST, e o resultado em streaming na resposta costuma usar o protocolo
text/event-stream. Mas como na prática não há mudança de estado, tecnicamente isso não se encaixa tão bem, e oEventSourceteimosamente só aceita GET. Por isso muitas APIs acabam reimplementando a mesma funcionalidade com um parser próprioAo ver
GET: Content (body) "no defined semantics", pensei que talvez não houvesse problema em permitir corpo no método GET, mas pela especificação original o corpo de GET deve ser completamente ignoradoAlém disso, se uma parte importante da requisição estiver num corpo que acaba sendo removido, o cache também pode quebrar
Se você adicionar parâmetros no corpo do GET, duas requisições com o mesmo URI deixam de poder ser tratadas como apontando para a mesma coisa, o que quebra essa restrição do método