RFC 10008: el nuevo método HTTP QUERY
(rfc-editor.org)- RFC 10008 define el método HTTP QUERY, que devuelve resultados después de que el recurso de destino procese la consulta incluida en el cuerpo de la solicitud de forma segura e idempotente
- QUERY combina la naturaleza safe/idempotent de GET con la forma de enviar cuerpo de POST, reduciendo URI largos, el costo de codificación en URI, la exposición en logs y la carga de tratar cada combinación de consulta como un recurso distinto
- El servidor solo puede procesarla si el Content-Type y el cuerpo de la solicitud son consistentes; tipos no soportados, discrepancias entre tipo y cuerpo, y consultas no procesables pueden distinguirse con diferentes respuestas 4xx
- Una respuesta exitosa puede indicar con Content-Location el recurso que representa el resultado de la consulta, y con Location un equivalent resource para volver a ejecutar la misma consulta
- Las respuestas a QUERY pueden cachearse, pero la clave de caché debe incluir también el cuerpo y los metadatos; en entornos CORS, como no es un método safelisted, requiere preflight
El patrón de consultas HTTP que QUERY busca resolver
- RFC 10008 es un documento de Internet Standards Track que define el método de solicitud QUERY de HTTP
- QUERY solicita que el recurso de destino procese el cuerpo de la solicitud y responda con el resultado
- Usa un cuerpo como POST, pero se define como safe e idempotent, por lo que permite reintentos o reinicios automáticos
- En las consultas tradicionales con GET, lo normal es poner la entrada en la URI
GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
- Cuando los datos de consulta se ponen en la URI, aumentan las limitaciones conforme crecen los datos
- Como la solicitud pasa por varios sistemas independientes, es difícil conocer de antemano el límite real de tamaño de URI
- HTTP recomienda que emisor y receptor soporten al menos 8000 octets, pero eso no garantiza a todos los sistemas en la ruta
- Algunos datos tienen un costo alto para codificarse como URI válida
- Es más probable que la URI de la solicitud quede en logs o se incluya en marcadores que el cuerpo de la solicitud
- Si la consulta se codifica directamente en la URI, cada combinación posible de entradas se trata como un recurso separado
Un método que aclara el significado entre GET y POST
- Muchas implementaciones envían consultas en el cuerpo de POST en lugar de usar GET
POST /feedContent-Type: application/x-www-form-urlencoded- Cuerpo:
q=foo&limit=10&sort=-published
- Con ese enfoque, sin conocer el recurso específico y el servidor, no queda claro si es una consulta segura e idempotente
- QUERY envía la misma entrada en el cuerpo de la solicitud, pero el propio método es seguro e idempotente
QUERY /feedContent-Type: application/x-www-form-urlencoded- Cuerpo:
q=foo&limit=10&sort=-published
- Gracias a esta semántica explícita, es más fácil aplicar funciones HTTP como caché y reintentos automáticos
- El servidor puede asignar una URI a la propia consulta o al resultado de una consulta específica para usarla después con GET
Reglas clave del método QUERY
- QUERY se usa para iniciar una consulta del lado del servidor
- GET solicita una representación del recurso identificado por la URI de destino, mientras que QUERY solicita realizar una operación de consulta dentro del alcance del recurso de destino
- El cuerpo de la solicitud y su tipo de medio definen la consulta, y el origin server determina el alcance de la operación en función del recurso de destino
- El servidor debe hacer fallar la solicitud si no hay campo de solicitud Content-Type o si no coincide con el cuerpo solicitado
- La query part de la URI de destino participa en la identificación del recurso consultado, igual que en cualquier método HTTP
- Si esa query part afecta directamente el resultado y de qué manera depende del comportamiento del recurso y queda fuera del alcance de esta especificación
- QUERY es seguro desde la perspectiva del recurso de destino
- El cliente no solicita ni espera cambios en el estado del recurso de destino
- No está prohibido que el servidor cree recursos HTTP para consultar información adicional
- QUERY es idempotente, por lo que puede repetirse o reintentarse si es necesario tras un fallo de conexión
- Una respuesta
200 OKindica que la consulta se procesó correctamente y que el resultado está incluido en el cuerpo de la respuesta
Tipos de medio, negociación y manejo de errores
- La semántica de una solicitud QUERY depende del cuerpo de la solicitud y de metadatos relacionados, como el tipo de medio
- Las solicitudes en las que el cuerpo y los metadatos no son consistentes normalmente deben rechazarse con un 4xx Client Error
- El manejo de errores varía según dónde esté el problema en la solicitud
- Si no hay información de tipo de medio, la solicitud es inválida por definición y debe fallar con un código 4xx como
400 - Si el tipo de medio está especificado pero el recurso no lo soporta, corresponde
415 Unsupported Media Type - Aunque el tipo de medio sea conocido en principio, si no tiene semántica QUERY para el recurso de destino, también corresponde
415 - Si el tipo de medio no coincide con el cuerpo real de la solicitud, se puede devolver
400 Bad Request - El servidor no puede hacer content sniffing del cuerpo para inferir el tipo de medio y sobrescribir un valor ausente o incorrecto
- Si el tipo y el cuerpo coinciden, pero el contenido real de la consulta no puede procesarse, puede usarse
422 Unprocessable Content - Un ejemplo de
422es una consulta SQL sintácticamente válida que apunta a una tabla inexistente - Si el recurso no soporta el tipo de medio de respuesta solicitado por el cliente con
Accept, corresponde406 Not Acceptable
- Si no hay información de tipo de medio, la solicitud es inválida por definición y debe fallar con un código 4xx como
- El campo de respuesta
Accept-Querypuede informar al cliente qué tipos de medio de consulta están soportados
equivalent resource, Content-Location y Location
- Un equivalent resource es un recurso que representa una solicitud QUERY específica y su destino, y responde a solicitudes GET
- El equivalent resource toma en cuenta tanto el cuerpo de la solicitud como sus metadatos
- Esto incluye metadatos de representación, como el tipo de medio del cuerpo
- El servidor puede asignar una URI a un equivalent resource, pero no está obligado a hacerlo
- Una respuesta exitosa puede incluir en el encabezado Content-Location un identificador del recurso correspondiente al resultado de la consulta
- El cliente puede enviar un GET a la URI indicada para recuperar el resultado de la operación de consulta recién ejecutada
- Ese recurso puede ser temporal
- Una respuesta exitosa puede incluir en el encabezado Location la URI del equivalent resource de la solicitud QUERY
- El cliente puede enviar un GET a la URI indicada para repetir la misma operación de consulta sin reenviar el cuerpo de la consulta
- Esa URI también puede ser temporal
- Si una solicitud posterior falla, el cliente puede volver a intentar con el destino QUERY original y el cuerpo enviado previamente
Redirecciones y solicitudes condicionales
- El servidor puede optar por una respuesta indirecta que redirija el user agent a otra URI para una solicitud QUERY
301 Moved Permanentlyy308 Permanent Redirectindican que el recurso de destino se movió de forma permanente a otra URI apuntada por Location302 Foundy307 Temporary Redirectsignifican un movimiento temporal del recurso de destino- En los cuatro casos, el servidor sugiere que la solicitud original puede realizarse enviando una solicitud QUERY similar a la nueva URI de destino
- La excepción que redirige POST a GET después de
301o302no aplica a las solicitudes QUERY 303 See Otherpara QUERY indica que la consulta original puede realizarse como una solicitud de recuperación normal al URI indicado por Location- En HTTP eso significa enviar una solicitud GET a la nueva URI de destino
- En QUERY condicional, la selected representation es la misma que la de un GET al equivalent resource de esa solicitud QUERY
- El cliente puede pedir que el resultado de la consulta solo se devuelva en la respuesta si se cumplen las condiciones especificadas por los encabezados condicionales
Caché y solicitudes Range
- Las respuestas al método QUERY son cacheables, y la caché puede usarse para satisfacer solicitudes QUERY posteriores
- La clave de caché de una solicitud QUERY debe incluir el cuerpo de la solicitud y sus metadatos relacionados
- La caché puede eliminar diferencias semánticamente irrelevantes al generar la clave de caché
- eliminación de content encoding
- normalización según convenciones de formato indicadas por sufijos de subtipo de medio como
+json - normalización según la semántica del cuerpo indicada por
Content-Type
- Estas transformaciones son solo para generar la clave de caché y no modifican la solicitud en sí
- El cliente puede indicar con la directiva de caché
no-transformque no desea tales transformaciones, pero esta directiva es advisory - El caché de respuestas QUERY es inherentemente más complejo que el de GET
- Para determinar la clave de caché, hay que leer todo el cuerpo de la solicitud
- Si la respuesta QUERY proporciona una URI de equivalent resource en Location, el cliente puede pasar luego a GET para simplificar el manejo
- La semántica de Range Request para QUERY es la misma que para GET
- El único range unit definido al momento de redactarse, Byte Range Request, aporta poco valor para resultados de QUERY
- Con frecuencia, el propio formato de consulta ya ofrece limitación de resultados o paginación, como
FETCH FIRST ... ROWS ONLYen SQL, y se espera que se usen esas funciones integradas
Encabezado de respuesta Accept-Query
- El encabezado de respuesta Accept-Query informa directamente que un recurso soporta el método QUERY e identifica los tipos de medio de consulta que pueden usarse
- Accept-Query es una lista de media ranges que usa la sintaxis de Structured Fields
- Un media range se expresa como un List Structured Header Field de Token o String que contiene un valor media range sin parámetros
- Los parámetros del tipo de medio se mapean como Structured Field Parameters
- La elección entre String y Token no tiene importancia semántica
- El receptor puede convertir Token a String, pero no debe tratarlos de forma distinta según el tipo recibido
- Los tipos de medio no se mapean exactamente a Token, y cuando se permite un número inicial debe usarse la forma String
- Los únicos wildcard soportados son
*/*oxxxx/* - El orden de los tipos listados en el valor del campo no importa
- El valor de Accept-Query aplica a todas las URI del servidor que comparten el mismo path, y se ignora el query component
- Si solicitudes al mismo recurso devuelven distintos valores de Accept-Query, se usa el valor fresh más reciente recibido
- El ejemplo es el siguiente
Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"
- Accept-Query se parece a
Accept, pero como es un Structured Field debe seguir las reglas de procesamiento de Structured Fields de RFC 9651
Consideraciones de seguridad y CORS
- QUERY sigue todas las consideraciones generales de seguridad para métodos HTTP definidas en RFC 9110
- QUERY puede usarse como alternativa a poner la información de la solicitud en el query component de la URI
- Como las URI tienen más probabilidad de quedar en logs o de ser procesadas por intermediarios que los cuerpos de solicitud, QUERY puede ser preferible a GET para consultas con información sensible
- Si el servidor crea un recurso temporal para representar el resultado de QUERY y le asigna una URI, no debe incluir en esa URI partes sensibles si el cuerpo original contenía información que no debería quedar en logs
- Si una caché normaliza incorrectamente el cuerpo de QUERY o lo normaliza de forma muy distinta a como lo procesa el recurso, puede devolver una respuesta equivocada por false positive
- Las solicitudes QUERY de user agents que implementan CORS requieren una solicitud preflight
- QUERY no forma parte del conjunto de CORS-safelisted methods
Registro en IANA y elección del nombre del método
- IANA agrega el método QUERY al HTTP Method Registry
- Method Name:
QUERY - Safe:
yes - Idempotent:
yes - Specification: RFC 10008 Section 2
- Method Name:
- IANA agrega el campo Accept-Query al HTTP Field Name Registry
- Field Name:
Accept-Query - Status:
permanent - Structured Type:
List
- Field Name:
- En el HTTP Method Registry ya existían
PROPFIND,REPORTySEARCH, con propiedades safe e idempotent - En etapas iniciales se usó
SEARCH, pero el nombre final del método pasó a ser QUERY - QUERY fue elegido por las siguientes razones
- Las alternativas usaban el tipo de medio general
application/xmlen el cuerpo de la solicitud y el significado de la solicitud dependía por completo del cuerpo - Todas las alternativas surgieron de actividades de WebDAV
QUERYcaptura bien la relación con el query component de la URI
- Las alternativas usaban el tipo de medio general
1 comentarios
Comentarios en Hacker News
Habría sido más convincente si hubiera habido un ejemplo con motivación fuerte; usaron ejemplos que se pueden expresar demasiado fácil con GET y eso más bien distrae
Incluso si imaginas un QUERY con una gran estructura de filtros JSON o una imagen de entrada en el cuerpo de la solicitud, se siente muy raro que el cuerpo de la solicitud forme parte de la clave de caché. Aparecen claves de caché infinitas controladas por el usuario, y las estrategias de caché normales en la práctica solo pueden comparar el cuerpo bit a bit o hacerle hash, así que en escenarios maliciosos se vuelve muy fácil invalidar la caché
Si construyes un servicio que necesita filtrado complejo o entradas complejas como imágenes, es muy probable que el caché esté muy lejos de la capa HTTP. Por ejemplo, podría basarse en columnas de datos individuales de un join, o en embeddings cuya clave sea el hash perceptual de una imagen decodificada, sin relación con la representación exacta en bits sobre el cable
No entiendo por qué querrían capturar esto a la fuerza de forma genérica. Me parece mucho mejor expresar la semántica de caché en POST con un encabezado nuevo como
"Vary: request-body". Sería totalmente retrocompatible, y fuera de ese 0.1% de casos de uso de CDN donde tal vez sirva, se puede ignorarNo se me ocurre ningún ataque relacionado con caché que no aplique igual a los parámetros de query. Si quieres saturar la caché, crear un parámetro de query único de 30 caracteres es tan fácil como crear un cuerpo de solicitud de 30 MB
En la práctica, un sistema para Internet pública usaría un hash criptográfico como clave de caché para que siempre tenga el mismo tamaño. Las claves de caché ya incluyen URLs que pueden ser larguísimas y conjuntos arbitrarios de valores de encabezado
Los GET con parámetros de query ya son opacos y facilitan la invalidación de caché
Pero creo que sería mejor tener QUERY seguido de POST, porque ya no serían simplemente la misma solicitud con un flag de seguridad, sino tipos de solicitud distintos
Me pregunto si los formularios HTML agregarán soporte para QUERY
QUERY debería ser idempotente, así que podría evitar la molesta advertencia de reenvío al refrescar la página de resultado de un envío de formulario POST
Si se agrega
method=QUERY, aparecerá otra variante nueva de esa rarezaPara quienes todavía quieran fingir que seguimos en el siglo pasado: https://www.rfc-editor.org/rfc/rfc10008.txt
“La opción de adjuntar un cuerpo a una solicitud GET fue estudiada a fondo por el grupo de trabajo de IETF, pero al final se decidió crear el nuevo método QUERY. La decisión de crear un método aparte se debió a problemas históricos de interoperabilidad y al estricto apego a la definición arquitectónica central de HTTP”
Bueno, yo llevo años enviando cuerpos de solicitud con el método GET
fetchhttps://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U...
“No puedes incluir un cuerpo en solicitudes GET”
También puede causar problemas raros por el caché transparente
Sorprende que ya hayamos llegado a números RFC de 5 dígitos
Alguien hizo una apuesta ambigua sobre cuándo se publicaría el RFC 10000, pero la numeración saltó directamente de 9998 a 10008. Nadie ganó
https://manifold.markets/CollectedOverSpread/when-will-rfc-1...
Si para consultar resultados de búsqueda en HTTP hay que usar el método QUERY y no agregar parámetros de query, el nombre termina siendo confuso
El término
queryya se usa para referirse a las solicitudes HTTP en generalSolo con ver el título del RFC ya me confundí
querypara referirse a las solicitudes HTTP en general? Coloquialmente a veces se llama consulta a una solicitud GET, pero jamás a POST, PUT o DELETEEdit: ah, ya entendí, declararon QUERY como un método “seguro”, sin efectos colaterales, por la posibilidad de caché. Me confundí
Si esto realmente llegara a reemplazar en la práctica a las solicitudes GET con query string, de verdad espero que los marcadores del navegador soporten preservar los parámetros de la solicitud
Sé que esto queda fuera del alcance de este RFC, pero me gusta que una extensión sencilla de esto podría hacer que
EventSourcetambién funcione para consultas de IA en streamingComo la solicitud necesita cuerpo, todos usan POST, y para los resultados en streaming a menudo usan el protocolo
text/event-streamen la respuesta. Pero en realidad no hay cambio de estado, así que técnicamente no encaja del todo, yEventSourcede forma obstinada solo puede usar GET. Por eso muchas APIs reimplementan la misma funcionalidad con su propio parserAl ver
GET: Content (body) "no defined semantics", pensé que no estaría mal permitir cuerpos en GET, pero según la especificación original el cuerpo en GET debe ignorarse por completoAdemás, si una parte importante de la solicitud va en un cuerpo que luego se descarta, también se puede romper el caché
Si agregas parámetros en el cuerpo a GET, dos solicitudes con el mismo URI ya no pueden tratarse como si apuntaran a lo mismo, y eso rompe las restricciones de este método