HTTP 终于有了带 body 的安全方法:QUERY

GET 不能带 body,POST 又不敢重试 —— 查询接口被这个两难卡了二十年。IETF 的 QUERY 草案给出了第三个选项:安全、幂等、且能带请求体。这篇讲它解决什么、代价是什么、现在能不能用。

查询接口一直卡在一个两难里:GET 表达不了复杂查询,POST 表达错了语义。

IETF 的 HTTP 工作组在 2025 年 11 月出了一版草案(draft-ietf-httpbis-safe-method-w-body-14),定义了一个新方法 QUERY。一句话概括:

QUERY 请求让目标资源以安全且幂等的方式处理请求体,然后返回处理结果。它和 POST 相似,但可以被自动重放或重启,不必担心留下半截状态。

问题:查询要塞进 URL 里

GET 的语义是对的 —— 它安全、幂等、可以被缓存、可以被代理自动重试。但它的参数只能放 URL:

  • 一个带十几个过滤条件的搜索,URL 很容易到几千字符,撞上各种 8KB 上限
  • URL 会进访问日志、进 Referer、进浏览器历史 —— 查询条件里往往有不该留痕的东西
  • 嵌套结构(比如图查询、布尔表达式树)转成 query string 之后可读性归零

标准给的答案是「那你用 POST」。但 POST 的语义是「这个请求可能改变状态」,于是:

  • 代理、CDN、客户端 HTTP 库默认不敢重试 —— 一次网络抖动就可能让你手动再点一次
  • 缓存中间层不知道该不该缓存
  • 幂等的查询被标成了非幂等,纯粹是表达能力的锅

QUERY 填的就是这个空

草案给它的定位很克制:不是新能力,是给「带 body 的查询」一个语义正确的方法。

GET POST QUERY
请求体 不该有 有 有
安全(不改变状态) 是 否 是
幂等 是 否 是
可自动重放 / 重启 是 否 是

安全 + 幂等这两条,正是中间层敢不敢自动重试的全部依据。QUERY 把这两条拿回来了,同时保留了请求体。

顺带一提,草案的讨论在 ietf-http-wg@w3.org 邮件列表,源码与 issue 在 httpwg/http-extensions 仓库的 query-method 标签下。这一版 2026 年 5 月 22 日过期 —— 也就是它还在改,别按它写生产代码。

谁最需要它

搜索服务。Elasticsearch 的 _search 早就支持 GET 带 body 了 —— 这是一个非标准的、各家心照不宣的绕法。它工作得很好,代价是任何严格按 RFC 实现的中间层都可能把那个 body 丢掉。QUERY 是把这件事扶正。

GraphQL。它一直是 POST,因为查询本身可能很大。但 GraphQL 同时有 query 和 mutation,所以不能整站换 —— 能换的是「只读的那部分」,而这恰好是流量最大的部分。

任何 RPC 风格的读接口。如果参数结构复杂到要用 JSON 表达,而操作本身是只读的,POST 就是在撒谎。

现实的阻力

语义正确不等于马上能用。三个坎:

  1. 中间层要认这个方法。老的代理、WAF、部分 CDN 见到不认识的 method 会直接 405。它们不会因为你的接口语义正确就放行。
  2. 会触发 CORS 预检。QUERY 不在简单方法之列,跨域场景下每个请求前都多一次 OPTIONS。
  3. 浏览器与客户端库的支持面。fetch 传任意 method 没问题,但各种封装(axios、requests、Go 的 http 包)对非标准方法的处理并不一致。

我的判断

QUERY 的价值不在「能做新事」,而在让已有的做法不再依赖默契。

短期我不会拿它做公开 API 的唯一入口 —— 405 的风险是实打实的。但内部服务之间、以及我能控制两端和中间层的场景,它比 POST 更诚实。

值得盯的两个信号:草案变成 RFC,以及主流反向代理开始默认放行它。在那之前,把它当成一个「知道就好」的选项,比急着上更划算。

← 返回文章列表

评论

…