← 返回日报
精读 预计 2 分钟

RFC 10008: The HTTP QUERY Method

摘要

RFC 10008(2026-06-15 发布)定义了 HTTP 方法 QUERY,允许请求携带 body,同时语义上仍被视为安全、幂等且可缓存。文章指出,这主要解决了 API 设计中的长期矛盾:GET 不能带请求体,而 POST 虽能带 body,但会被基础设施当作可能有副作用的请求,从而影响重试、CDN 缓存与中间层优化,尤其在 JSON - RPC 这类 “语义是查询但形式是 POST” 的场景中尤为明显。QUERY 的语义是 “发送查询描述,返回结果”,从缓存角度等同于 GET。实现示例包括 Go(net / http 直接支持自定义 method 字符串)与 Rust(reqwest 可通过 Method 指定)。RFC 重点改进在缓存机制:缓存键必须包含请求 body 及其元数据(如 Content-Type),允许 CDN 或反向代理基于 body 内容做缓存匹配,并可对 JSON 进行规范化(如 key 排序、忽略空白)以提升命中率。文章认为这使得原本依赖 POST 的只读 RPC API 可以直接获得 HTTP 原生缓存能力,减少自定义缓存层需求。

荐读理由

RFC 10008 定义的 QUERY 方法(接受带请求体的 HTTP 请求),Go 标准库 net/http 直接支持,无需依赖;缓存 key 必须包含请求体及元数据,支持 reverse proxy/CDN 原生缓存 JSON,改变 RPC 样式接口的结构化读取问题

原文

RFC 10008: The HTTP QUERY Method

June 17 2026

RFC 10008 was published on June 15, 2026 and defines a new HTTP method: QUERY. It fills a gap that has existed for as long as I have been building APIs. You have data to send to the server in order to describe what you want back, but GET does not have a body and POST is neither safe nor idempotent. QUERY gives you a method that accepts a request body while remaining safe, idempotent, and cacheable.

If you have ever built an SDK that talks to a JSON-RPC API you have felt this pain. JSON-RPC by design sends a JSON payload describing the method and parameters. That payload has to go in the body, which means POST, which means caches and intermediaries treat every request as a state-changing operation. Retry logic gets complicated. CDN caching is off the table. You end up building your own application-level caching because HTTP's built-in mechanisms cannot help you.

QUERY changes that. The semantics are simple: send a body, get a response, and the whole exchange is treated like a GET from the perspective of caching and safety.

In Go

Go's net/http already lets you use arbitrary method strings with http.NewRequest, so SDK code using QUERY looks about like you would expect:

body, _ := json.Marshal(map[string]any{
	"jsonrpc": "2.0",
	"method":  "getScore",
	"params":  []any{"0xABC123", "latest"},
	"id":      1,
})

req, _ := http.NewRequestWithContext(ctx, "QUERY", "https://rpc.example.com", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)

No new dependencies needed. The standard library handles it because HTTP methods are just strings.

In Rust

With reqwest you can use reqwest::Method to define a custom method:

use reqwest::{Client, Method};

let client = Client::new();
let query_method = Method::from_bytes(b"QUERY").unwrap();

let resp = client
    .request(query_method, "https://rpc.example.com")
    .header("Content-Type", "application/json")
    .body(r#"{"jsonrpc":"2.0","method":"getScore","params":["0xABC123","latest"],"id":1}"#)
    .send()
    .await?;

Caching

The part I have been waiting for is in Section 2.7. The response to a QUERY is cacheable and the cache key must incorporate the request body and its metadata. This means a reverse proxy or CDN can look at the Content-Type and the body bytes together and serve a cached response for identical queries. Caches can also normalize the body (reorder JSON keys, strip insignificant whitespace) to improve hit rates.

For RPC-style APIs where every request is semantically a read operation but structurally a POST, this is a meaningful improvement. You get HTTP-native caching without building a bespoke layer on top.

The RFC is short and readable. Worth 20 minutes if you build or consume HTTP APIs.

Lobsters · 3 赞 · 0 评 讨论 → 阅读原文 →

这条对你有帮助吗?