1 Punkte von GN⁺ 4 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • RFC 10008 definiert die HTTP-Methode QUERY, bei der die Zielressource eine im Request-Body enthaltene Anfrage sicher und idempotent verarbeitet und anschließend das Ergebnis zurückgibt
  • QUERY kombiniert den safe/idempotent-Charakter von GET mit der Body-Übertragung von POST und verringert so Probleme mit langen URIs, URI-Encoding-Kosten, Offenlegung in Logs und dem Aufwand, jede Anfragekombination als eigene Ressource zu behandeln
  • Ein Server kann einen QUERY-Request nur verarbeiten, wenn Content-Type und Body konsistent sind; nicht unterstützte Typen, inkonsistente Bodies und inhaltlich nicht verarbeitbare Anfragen können jeweils mit unterschiedlichen 4xx-Antworten unterschieden werden
  • Eine erfolgreiche Antwort kann mit Content-Location eine Ergebnisressource der Anfrage und mit Location eine equivalent resource angeben, über die sich dieselbe Anfrage erneut ausführen lässt
  • QUERY-Antworten sind cachebar, aber der Cache-Schlüssel muss auch Body und Metadaten umfassen; in CORS-Umgebungen ist wegen fehlender Einstufung als safelisted method ein preflight erforderlich

HTTP-Anfragemuster, die QUERY lösen soll

  • RFC 10008 ist ein Internet-Standards-Track-Dokument, das die HTTP-Request-Methode QUERY definiert
  • QUERY fordert die Zielressource auf, den Request-Body zu verarbeiten und das Ergebnis in der Antwort zurückzugeben
  • Wie POST verwendet QUERY einen Body, ist aber als safe und idempotent definiert, sodass automatische Wiederholungen oder Neustarts möglich sind
  • Bestehende GET-Anfragen übergeben Eingaben üblicherweise in der URI
    • GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
  • Wenn Anfragedaten in die URI gelegt werden, steigen die Einschränkungen mit der Datenmenge
    • Da mehrere unabhängige Systeme beteiligt sein können, ist die tatsächliche URI-Größenbeschränkung im Voraus oft schwer abzuschätzen
    • HTTP empfiehlt, dass Sender und Empfänger mindestens 8000 octets unterstützen, garantiert dies aber nicht für alle Systeme auf dem Übertragungsweg
    • Manche Daten lassen sich nur mit hohem Aufwand in eine gültige URI encodieren
    • Request-URIs landen eher in Logs oder Bookmarks als Request-Bodies
    • Werden Anfragen direkt in die URI encodiert, wird jede mögliche Eingabekombination als separate Ressource behandelt

Eine Methode, die die Semantik zwischen GET und POST klarer macht

  • Viele Implementierungen übertragen Anfragen nicht per GET, sondern im POST-Body
    • POST /feed
    • Content-Type: application/x-www-form-urlencoded
    • Body: q=foo&limit=10&sort=-published
  • Ohne Wissen über die konkrete Ressource und den Server ist dabei nicht erkennbar, ob es sich um eine sichere und idempotente Anfrage handelt
  • QUERY sendet dieselben Eingaben im Request-Body, ist aber bereits auf Methodenebene sicher und idempotent
    • QUERY /feed
    • Content-Type: application/x-www-form-urlencoded
    • Body: q=foo&limit=10&sort=-published
  • Diese explizite Semantik erleichtert es, HTTP-Funktionen wie Caching und automatische Retries anzuwenden
  • Der Server kann der Anfrage selbst oder einem bestimmten Anfrageergebnis eine URI zuweisen, die später per GET genutzt werden kann

Zentrale Regeln der QUERY-Methode

  • QUERY wird verwendet, um serverseitige Anfragen zu starten
  • GET fordert eine Repräsentation der durch die Ziel-URI identifizierten Ressource an, während QUERY die Ausführung einer Anfrageoperation innerhalb des Geltungsbereichs der Zielressource verlangt
  • Request-Body und Medientyp definieren die Anfrage, und der origin server legt den Operationsbereich relativ zur Zielressource fest
  • Der Server muss den Request fehlschlagen lassen, wenn das Request-Feld Content-Type fehlt oder nicht zum Request-Body passt
  • Der query part der Ziel-URI beteiligt sich wie bei allen HTTP-Methoden an der Identifikation der Zielressource
    • Ob und wie dieser query part das Ergebnis direkt beeinflusst, ist ressourcenspezifisches Verhalten und liegt außerhalb dieser Spezifikation
  • QUERY ist aus Sicht der Zielressource safe
    • Der Client fordert keine Änderung des Zustands der Zielressource an und erwartet sie auch nicht
    • Dass der Server HTTP-Ressourcen anlegt, über die zusätzliche Informationen abgerufen werden können, ist nicht verboten
  • QUERY ist idempotent und kann daher nach Verbindungsfehlern bei Bedarf erneut gesendet oder wiederholt werden
  • Eine Antwort mit 200 OK zeigt an, dass die Anfrage erfolgreich verarbeitet wurde und das Ergebnis im Response-Body enthalten ist

Medientypen, Aushandlung und Fehlerbehandlung

  • Die Bedeutung eines QUERY-Requests hängt vom Request-Body und zugehörigen Metadaten wie dem Medientyp ab
  • Requests, bei denen Body und Metadaten nicht konsistent sind, sollten in der Regel mit einem 4xx Client Error abgelehnt werden
  • Die Fehlerbehandlung hängt davon ab, an welcher Stelle der Request fehlerhaft ist
    • Fehlt die Medientyp-Information, ist der Request definitionsgemäß fehlerhaft und sollte mit einem 4xx-Statuscode wie 400 fehlschlagen
    • Ist ein Medientyp angegeben, wird aber von der Ressource nicht unterstützt, ist 415 Unsupported Media Type passend
    • Auch wenn der Medientyp grundsätzlich bekannt ist, aber für die QUERY-Semantik der Zielressource keine Bedeutung hat, ist 415 angemessen
    • Wenn der Medientyp nicht zum tatsächlichen Request-Body passt, kann 400 Bad Request zurückgegeben werden
    • Der Server darf kein content sniffing betreiben, um anhand des Bodys einen fehlenden oder falschen Wert zu erraten und zu überschreiben
    • Stimmen Typ und Body überein, ist die konkrete Anfrage aber inhaltlich nicht verarbeitbar, kann 422 Unprocessable Content verwendet werden
    • Ein Beispiel für 422 ist eine syntaktisch korrekte SQL-Anfrage, die auf eine nicht existierende Tabelle verweist
    • Wenn die vom Client per Accept angeforderten Response-Medientypen von der Ressource nicht unterstützt werden, ist 406 Not Acceptable passend
  • Das Response-Feld Accept-Query kann dem Client mitteilen, welche Anfragemedientypen unterstützt werden

equivalent resource, Content-Location, Location

  • Eine equivalent resource ist eine Ressource, die einen bestimmten QUERY-Request und dessen Ziel repräsentiert und auf GET-Requests antwortet
  • Eine equivalent resource berücksichtigt sowohl den Request-Body als auch die Metadaten
    • Dazu gehören Repräsentationsmetadaten wie der Medientyp des Bodys
  • Der Server kann einer equivalent resource eine URI zuweisen, ist dazu aber nicht verpflichtet
  • Eine erfolgreiche Antwort kann im Header Content-Location einen Ressourcenbezeichner für das Anfrageergebnis enthalten
    • Der Client kann ein GET an die angegebene URI senden, um das Ergebnis der gerade ausgeführten Anfrageoperation abzurufen
    • Diese Ressource kann temporär sein
  • Eine erfolgreiche Antwort kann im Header Location die URI der equivalent resource des QUERY-Requests enthalten
    • Der Client kann dann ohne erneutes Senden des Anfrage-Bodys ein GET an die angegebene URI senden und dieselbe Anfrageoperation wiederholen
    • Auch diese URI kann temporär sein
    • Falls eine spätere Anfrage fehlschlägt, kann der Client es mit dem ursprünglichen QUERY-Ziel und dem zuvor gesendeten Body erneut versuchen

Weiterleitungen und bedingte Requests

  • Der Server kann auf einen QUERY-Request mit einer indirekten Antwort reagieren, die den User Agent auf eine andere URI umleitet
  • 301 Moved Permanently und 308 Permanent Redirect zeigen an, dass die Zielressource dauerhaft an eine andere durch Location angegebene URI verschoben wurde
  • 302 Found und 307 Temporary Redirect bedeuten eine temporäre Verschiebung der Zielressource
  • In allen vier Fällen schlägt der Server vor, dass der ursprüngliche Request durch Senden eines entsprechenden QUERY-Requests an die neue Ziel-URI ausgeführt werden kann
  • Die Ausnahme, nach 301 oder 302 einen POST in GET umzuwandeln, gilt nicht für QUERY-Requests
  • 303 See Other für QUERY zeigt an, dass die ursprüngliche Anfrage als normaler Abrufrequest an die in Location angegebene URI ausgeführt werden kann
    • In HTTP wird dabei ein GET-Request an die neue Ziel-URI gesendet
  • Bei bedingten QUERY-Requests ist die selected representation dieselbe wie bei einem GET auf die equivalent resource dieses QUERY-Requests
  • Der Client kann verlangen, dass ein Anfrageergebnis nur dann in der Antwort zurückgegeben wird, wenn die durch bedingte Header angegebenen Bedingungen erfüllt sind

Caching und Range-Requests

  • Antworten auf die QUERY-Methode sind cachebar, und der Cache kann verwendet werden, um spätere QUERY-Requests zu erfüllen
  • Der Cache-Schlüssel eines QUERY-Requests muss den Request-Body und zugehörige Metadaten umfassen
  • Der Cache darf Unterschiede entfernen, die für die Semantik des Cache-Schlüssels nicht relevant sind
    • Entfernen von content encoding
    • Normalisierung entsprechend Formatkonventionen, die durch einen Mediensubtyp-Suffix wie +json angezeigt werden
    • Normalisierung entsprechend der durch Content-Type beschriebenen Body-Semantik
  • Diese Transformationen dienen ausschließlich der Erzeugung des Cache-Schlüssels und verändern den Request selbst nicht
  • Mit der Cache-Direktive no-transform kann der Client angeben, dass solche Transformationen unerwünscht sind, diese Direktive ist jedoch advisory
  • Das Caching von QUERY-Antworten ist inhärent komplexer als bei GET
    • Um den Cache-Schlüssel zu bestimmen, muss der gesamte Request-Body gelesen werden
    • Wenn eine QUERY-Antwort per Location eine URI für eine equivalent resource liefert, kann der Client anschließend auf GET umsteigen und die Verarbeitung vereinfachen
  • Die Semantik von Range Request bei QUERY ist dieselbe wie bei GET
  • Die zum Zeitpunkt der Spezifikation einzige definierte range unit, Byte Range Request, ist für QUERY-Ergebnisse von geringem Nutzen
  • Häufig bietet bereits das Anfrageformat selbst eine Begrenzung oder Paginierung des Ergebnisses, etwa wie FETCH FIRST ... ROWS ONLY in SQL; die Nutzung solcher eingebauten Funktionen wird erwartet

Response-Header Accept-Query

  • Der Response-Header Accept-Query zeigt direkt an, dass eine Ressource die QUERY-Methode unterstützt, und identifiziert die nutzbaren Medientypen für Anfragen
  • Accept-Query ist eine Liste von media ranges unter Verwendung der Structured-Fields-Syntax
  • Eine media range wird als List Structured Header Field aus Tokens oder Strings mit media-range-Werten ohne Parameter dargestellt
  • Medientyp-Parameter werden auf Structured Field Parameters abgebildet
    • Die Wahl zwischen String und Token ist semantisch nicht wichtig
    • Ein Empfänger darf Tokens in Strings umwandeln, darf sie aber nicht je nach empfangenem Typ unterschiedlich behandeln
  • Medientypen lassen sich nicht exakt auf Token abbilden; wenn führende Ziffern erlaubt sein müssen, ist die String-Form zu verwenden
  • Unterstützte Wildcards sind nur */* oder xxxx/*
  • Die Reihenfolge der im Feldwert aufgelisteten Typen ist nicht wichtig
  • Der Accept-Query-Wert gilt für alle URIs eines Servers mit demselben path; die query component wird ignoriert
  • Wenn Requests an dieselbe Ressource unterschiedliche Accept-Query-Werte zurückgeben, wird der zuletzt empfangene frische Wert verwendet
  • Ein Beispiel lautet wie folgt
    • Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"
  • Accept-Query wirkt ähnlich wie Accept, ist aber ein Structured Field und muss daher nach den Structured-Fields-Regeln aus RFC 9651 verarbeitet werden

Sicherheitsaspekte und CORS

  • QUERY folgt allen allgemeinen Sicherheitsaspekten für HTTP-Methoden, wie sie in RFC 9110 definiert sind
  • QUERY kann als Alternative dazu verwendet werden, Request-Informationen in der URI query component unterzubringen
  • Da URIs eher protokolliert oder von Intermediären verarbeitet werden als Request-Bodies, kann QUERY bei sensiblen Informationen in Anfragen gegenüber GET vorteilhaft sein
  • Wenn ein Server eine temporäre Ressource erstellt, die ein QUERY-Ergebnis repräsentiert, und ihr eine URI zuweist, darf diese URI keine sensiblen Teile enthalten, falls der ursprüngliche Request-Body Informationen enthielt, die nicht in Logs erscheinen sollten
  • Wenn ein Cache QUERY-Bodies falsch normalisiert oder deutlich anders normalisiert als die Ressource selbst, kann er durch false positives falsche Antworten zurückgeben
  • QUERY-Requests von User Agents mit CORS-Implementierung erfordern einen preflight-Request
    • QUERY gehört nicht zur Menge der CORS-safelisted methods

IANA-Registrierung und Wahl des Methodennamens

  • IANA fügt die Methode QUERY dem HTTP Method Registry hinzu
    • Method Name: QUERY
    • Safe: yes
    • Idempotent: yes
    • Specification: RFC 10008 Section 2
  • IANA fügt das Feld Accept-Query dem HTTP Field Name Registry hinzu
    • Field Name: Accept-Query
    • Status: permanent
    • Structured Type: List
  • Im HTTP Method Registry gab es bereits PROPFIND, REPORT und SEARCH mit den Eigenschaften safe und idempotent
  • In frühen Phasen wurde SEARCH verwendet, der endgültige Methodenname wurde jedoch QUERY
  • QUERY wurde aus folgenden Gründen gewählt
    • Die Alternativen verwendeten im Request-Body den allgemeinen Medientyp application/xml, und die Request-Semantik hing vollständig vom Body ab
    • Die Alternativen entstammten sämtlich dem WebDAV-Umfeld
    • QUERY erfasst die Beziehung zur query component der URI besonders gut

1 Kommentare

 
GN⁺ 4 시간 전
Hacker-News-Kommentare
  • Mit einem starken Motivationsbeispiel wäre das überzeugender gewesen, aber stattdessen wurde ein Beispiel gewählt, das sich zu leicht als GET ausdrücken lässt, was eher ablenkt
    Selbst wenn man sich QUERY mit großen JSON-Filterstrukturen oder Bildeingaben im Request-Body vorstellt, fühlt es sich sehr seltsam an, dass der Request-Body Teil des Cache-Schlüssels wird. Damit entstehen unbegrenzt viele, nutzerkontrollierte Cache-Schlüssel, und übliche Caching-Strategien laufen praktisch darauf hinaus, den Request-Body bitweise zu vergleichen oder zu hashen, wodurch Cache-Invalidierung in böswilligen Szenarien sehr leicht wird
    Wenn man einen Dienst baut, der komplexe Filterung oder komplexe Eingaben wie Bilder braucht, liegt das Caching wahrscheinlich weit entfernt von der HTTP-Ebene. Der Schlüssel würde sich dann etwa an einzelnen Datenspalten eines Joins orientieren oder an Embeddings, die über einen Perzeptions-Hash der dekodierten Bildeingabe indiziert werden, also nicht an der exakten Bitdarstellung auf dem Draht
    Ich verstehe nicht, warum man so etwas unbedingt auf generische Weise abbilden will. Stattdessen wäre es viel besser, die Caching-Semantik bei POST mit einem neuen Header wie "Vary: request-body" auszudrücken. Das wäre vollständig abwärtskompatibel und könnte außer für die 0,1 % der CDN-Anwendungsfälle, in denen dieses Verhalten vielleicht nützlich ist, einfach ignoriert werden

    • Der Query-Teil der URI bei GET ist in der Praxis ebenfalls fast unbegrenzt, nutzerkontrolliert und Teil des Cache-Schlüssels. Deshalb ist mir nicht klar, warum das Gegenstück hier als besonders problematisch dargestellt wird
    • Wenn Browser kleinere Cache-Schlüssel wollen, können sie einfach einen kollisionsresistenten Hash des Bodys speichern, zum Beispiel mit SHA-256
      Mir fällt kein Angriff auf Caching ein, der nicht genauso auch auf Query-Parameter anwendbar wäre. Wenn man den Cache fluten will, ist es genauso einfach, einen eindeutigen Query-Parameter mit 30 Zeichen zu erzeugen wie einen 30-MB-Request-Body
    • Nicht alle Nutzungsszenarien sind das öffentliche Internet, und nur weil etwas dort nicht nützlich ist, heißt das nicht, dass es nicht standardisiert werden kann
      Realistisch betrachtet würden Systeme für das öffentliche Internet einen kryptographischen Hash als Cache-Schlüssel verwenden, damit dieser immer gleich groß ist. Cache-Schlüssel enthalten ohnehin bereits sehr lange URLs und beliebige Mengen an Header-Werten
    • Bilder kann man auch im Request-Body senden, aber das geht schon heute ebenso über base64-Query-Parameter. Wenn man will, kann man jeden vorgeschlagenen Standard missbräuchlich einsetzen
      Auch GET mit Query-Parametern ist bereits opak und macht Cache-Invalidierung einfach
    • Ich baue zum Beispiel gerade einen MCP-Server für eine Datenbank. In ChatGPT möchte ich vor einem Commit zunächst einen dry-run-POST machen, der anschließend zurückgerollt wird, aber beides sind POST-Requests, die sich nur in einer Eigenschaft unterscheiden, und dadurch wird ständig die Sicherheits-Schicht des Tools ausgelöst. Aus mehreren Gründen ist es außerdem schwer, die genaue Ursache zu debuggen
      Mit QUERY vor dem POST wäre das aber vermutlich besser, weil es dann nicht einfach derselbe Request mit einem Sicherheits-Flag wäre, sondern zwei unterschiedliche Request-Typen
  • Ich frage mich, ob HTML-Formulare QUERY-Unterstützung bekommen werden
    QUERY sollte idempotent sein und könnte deshalb die lästige Warnung vor erneutem Absenden vermeiden, die beim Neuladen von Ergebnisseiten nach POST-Formularübermittlungen erscheint

    • Ich wünsche mir seit Jahrzehnten, dass HTML-Formulare mehr Methoden als nur GET/POST unterstützen. Es gibt gerade einen WHATWG-Vorschlag, und wer das unterstützen möchte, kann hier mitmachen: https://github.com/whatwg/html/pull/11347
    • Eine Merkwürdigkeit bei Formularen ist, dass das Ergebnis eines Formular-POSTs eine Seite mit einem Ort (URL) ist, diese Seite aber nicht über diesen Ort geladen werden kann. Soweit ich weiß, wird die Tatsache, dass die Seite per POST und nicht per GET entstanden ist, nirgendwo gespeichert, wo Nutzer oder JS sie sehen könnten, und auch Neuladen verhält sich merkwürdig
      Wenn method=QUERY hinzukommt, entsteht davon nur noch eine weitere seltsame Variante
    • Das lässt sich besser mit dem POST-Redirect-GET-Muster lösen
    • Siehe https://github.com/whatwg/html/issues/12594
    • Bisher wurden nie andere Verben hinzugefügt, aber inzwischen ist es eine neue Zeit, also wer weiß
  • Für alle, die noch so tun wollen, als wäre noch das letzte Jahrhundert: https://www.rfc-editor.org/rfc/rfc10008.txt

    • Solche langen, vollständigen Plaintext-Dokumente werde ich wohl für immer lieben. Das erinnert mich an die guten alten Zeiten, als ich Videospiel-FAQs gelesen habe. In vielerlei Hinsicht ist das wirklich ein überlegenes Informationsformat
    • Die Formatierung ist wunderschön. Ich sollte das als Stilvorlage für interne Arbeitsmemos kopieren. Zeitlos
  • „Der Ansatz, GET-Requests mit einem Body zu versehen, wurde in der IETF-Arbeitsgruppe eingehend geprüft, aber letztlich entschied man sich stattdessen für die Einführung der neuen QUERY-Methode. Die Entscheidung für eine eigene Methode beruhte auf historischen Interoperabilitätsproblemen und der strikten Einhaltung der zentralen Architekturdefinitionen von HTTP.“
    Ich schicke allerdings schon seit Jahren Request-Bodys mit der GET-Methode

    • Manche Load Balancer sollen den Body verwerfen
    • Im Allgemeinen ist das keine gute Idee. In manchen HTTP-Implementierungen geht es überhaupt nicht, zum Beispiel bei fetch
      https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U...
      „GET requests cannot have a body.“
      Wegen transparentem Caching kann es auch zu seltsamen Problemen kommen
  • Es überrascht mich, dass wir jetzt schon bei 5-stelligen RFC-Nummern angekommen sind

  • Jemand hatte eine absichtlich vage Wette darauf abgeschlossen, wann RFC 10000 veröffentlicht wird, aber die Nummern sprangen direkt von 9998 auf 10008. Damit hat niemand gewonnen
    https://manifold.markets/CollectedOverSpread/when-will-rfc-1...

  • Wenn man in HTTP-Anfragen Suchergebnisse abfragen will, soll man also die QUERY-Methode verwenden und keine Query-Parameter hinzufügen
    Der Name ist verwirrend. Der Begriff query wird schließlich schon verwendet, um allgemein auf HTTP-Requests zu verweisen
    Schon der RFC-Titel hat mich verwirrt

    • In welchem Bereich wird query denn verwendet, um allgemein auf HTTP-Requests zu verweisen? Umgangssprachlich bezeichnet man GET-Requests manchmal als Anfragen, aber POST, PUT oder DELETE niemals so
    • Stimmt. Außerdem muss es nicht einmal zwingend eine Abfrage sein; es könnte auch ein idempotenter Effekt sein. Vielleicht wäre IPOST, also idempotentes POST, der bessere Name gewesen
      Korrektur: Ah, für die Cache-Fähigkeit wurde QUERY wohl als nebenwirkungsfreie, „sichere“ Methode deklariert. Mein Fehler
  • Falls das in freier Wildbahn tatsächlich GET-Requests mit Query-String ersetzen sollte, hoffe ich sehr, dass Browser-Lesezeichen das Bewahren von Request-Parametern unterstützen werden

    • Vermutlich eher nicht. Wahrscheinlicher ist, dass es Orte ersetzt, die derzeit POST für Abfragen verwenden
  • Ich weiß, das liegt außerhalb des Geltungsbereichs dieses RFC, aber nett daran ist, dass man das leicht erweitern könnte, sodass JS EventSource auch für streamende KI-Abfragen funktioniert
    Da der Request einen Body braucht, wird überall POST verwendet, und für Streaming-Ergebnisse kommt in der Response oft das Protokoll text/event-stream zum Einsatz. Tatsächlich ändert sich aber kein Zustand, also passt das technisch nicht besonders gut, und EventSource besteht hartnäckig darauf, nur GET zu verwenden. Deshalb implementieren viele APIs dieselbe Funktionalität mit eigenen Parsern noch einmal

  • Wenn ich GET: Content (body) "no defined semantics" sehe, denke ich zwar, dass es vielleicht okay wäre, beim GET-Method Body zu erlauben, aber laut ursprünglicher Spezifikation sollte ein GET-Body vollständig ignoriert werden
    Außerdem kann Caching kaputtgehen, wenn ein wichtiger Teil der Anfrage in einem Body steckt, der entfernt wird

    • GET nur mit URI hat die Semantik, die aktuelle Darstellung einer Ressource abzurufen. Das ist die grundlegendste Form eines Hyperlinks und ziemlich wichtig dafür, wie das Web funktioniert
      Wenn man GET um Body-Parameter erweitert, kann man zwei Requests mit derselben URI nicht mehr als Verweis auf dasselbe behandeln, und damit verletzt man genau diese Einschränkung der Methode