找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4337

积分

0

好友

567

主题
发表于 2 小时前 | 查看: 3| 回复: 0

HTTP 很少会新增一个方法。

对于大多数开发者来说,整个职业生涯里翻来覆去用的也就是那几个再熟悉不过的动词:

GET,用来获取数据。
POST,用来创建或提交数据。
PUT,用来替换资源。
DELETE,用来删除资源。

这些规则我们背得很熟。但在它们中间,其实一直存在着一个尴尬的空洞。

问题是:当你想进行一个带条件、筛选、排序、嵌套逻辑,甚至包含数组的复杂查询时,到底该用什么?

我说的不是那种“给我用户 ID 为 42 的信息”这种简单查询。那种情况用 GET 自然是干净利落的。我说的是另一种情况:给我所有角色是 admin、部门是 engineering 的用户,还要按姓名排序、分页返回,并限定日期范围。

这已经不是单纯地查一个资源了。这是一个复杂的读操作,而 HTTP 协议这么多年来,从来没给它一个真正合适的位置。

为什么 GET 本来就不适合做这件事?

很多人的第一反应是用 GET。毕竟你只是要读取数据,并没有修改任何东西,这个直觉本身没错。GET 是 safe 的,也是 idempotent 的。这意味着浏览器、缓存、代理服务器以及请求链路上的所有中间层,都明白一个道理:这个请求调用一次和调用两次,不应该产生不同的结果。它不会修改服务器状态,所以响应可以被安全地缓存。

但问题出在另一个层面:GET 把数据放在 URL 里。

比如,你要查 role 是 admin、department 是 engineering 的用户,请求可能长这样:

/users?role=admin&department=engineering

两个筛选条件,这看起来甚至很优雅。但如果你继续加条件呢?加入日期范围、排序字段、一个嵌套的“OR this AND that”逻辑,再加一个 ID 数组,很快你的 URL 就会变成一串难以阅读的编码字符。

再往前推一步,你还会撞上一个硬限制:浏览器和服务器对 URL 长度都有限制。一旦查询条件太复杂,请求可能还没进入业务逻辑,就直接触发了 414 URI Too Long 错误。

于是,有人开始想:那把参数放到 body 里总行了吧?这个想法听起来很合理,技术上,GET 请求在传输层确实可以带 body。但在现实世界中,这件事非常混乱。有些客户端会把 body 剥离,有些代理会直接丢弃它,还有些防火墙会阻断这类请求。不同的库、服务器、网关对其行为的处理都可能不同。

根本原因不是“有人忘了给 GET 加上 body 功能”,而是 HTTP 协议从未有过一个被广泛接受、明确标准化的 GET with body 的语义。因此,它是不可靠的。它不是一个隐藏特性,更像是一片“未定义行为”的沼泽。你可能碰巧用对了,但这并不意味着你应该依赖它。

那个一直在“撒谎”的临时方案

于是,开发者们做了当时唯一能做的事:用 POST 来做搜索。

从工程角度看,这确实能跑通。请求可以发出去,复杂的筛选条件也能安全地放在 body 里,复杂的 JSON 不用再费力塞进 URL。一切看起来都解决了。

但根本问题在于,POST 生来就不是为这事儿设计的。POST 的语义是改变服务器状态——创建记录、提交表单、触发副作用。换句话说,它既不是 safe,也不是天然 idempotent 的。

这会直接影响整个 HTTP 基础设施如何看待这个请求。缓存、CDN、浏览器、中间代理,都会把 POST 当作一个可能修改状态的请求。即使你的 POST /search 接口根本没有触碰数据库里的任何一行数据,浏览器不知道,代理不知道,CDN 也不知道。所以,它不会像 GET 那样自然地缓存响应,连接中断后也不能放心地重试。

结果就是,你得到了一个非常别扭的接口:它明明是在读数据,却穿着一件“我要修改数据”的外衣。方法本身在撒谎,而整个链路中的每一个组件,都会把这个谎言当真。

QUERY 来了

这个缺口,现在终于被补上了。2026 年 6 月 15 日,IETF 发布了 RFC 10008,正式定义了一个全新的 HTTP 方法:QUERY

这是自 2010 年 PATCH 方法进入标准轨道以来,首个进入标准轨道的全新 HTTP 方法。QUERY 做的事情非常直接:它继承了 POST 能携带 body 的能力,同时又保留了 GET 那套 safe 和 idempotent 的语义。

一个 QUERY 请求可以长这样:

QUERY /users HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "role": "admin",
  "department": "engineering",
  "sort": "name",
  "page": 1
}

你再也不用把复杂的查询硬塞进 query string,也不用把嵌套对象编码成一堆 URL 乱码,更不用担心 414 错误了。筛选条件就像在 POST 里一样,可以老老实实地躺在 body 里。

但它又和 POST 不同。QUERY 会明确地告诉路径上所有的代理、缓存和重试机制:这是一个安全的读取操作,可以重复执行,重复执行也不会产生额外的副作用。

RFC 还新增了一个响应头:Accept-Query。服务器可以通过它声明某个 endpoint 支持 QUERY 方法,以及它愿意接受哪些 body 格式。

有一点必须澄清:QUERY 并不是让 GET 终于可以带 body 了。GET 还是原来的 GET,没有任何变化。QUERY 是一个独立的新方法,它与 GETPOST 并驾齐驱,而非替代或扩展它们。如果你看到有人把这次更新解释成“现在 GET 请求可以有 body 了”,那是个很常见但并不准确的说法。QUERY 出现的根本原因,恰恰是因为 GET with body 从来都不可靠。所以,工作组没有去硬改一个语义已经非常明确的老方法,而是选择新增一个动词。

缓存终于有救了

这可能是 QUERY 最精妙的设计。它的响应是可以缓存的,但其缓存键不能像 GET 那样只看 URL,因为真正的查询条件在 body 里。

RFC 10008 的要求是:缓存键必须由请求的 body 和其元数据(包括 Content-Type)共同构成。这意味着,反向代理或 CDN 可以读取 body 字节和相关头部,当下一次进来一个完全一致的查询时,就能直接返回缓存的响应。

这才是复杂读取操作真正需要的缓存模型。以前那些基于 POST 的搜索接口,一直很难标准化地实现这一点。现在,QUERY 为它提供了一个名正言顺的位置。

现在谁已经开始支持了?

RFC 10008 的作者包括 J. Reschke、来自 Cloudflare 的 J.M. Snell 以及来自 Akamai 的 M. Bishop。Cloudflare 和 Akamai 这两家顶级 CDN 公司共同参与这个规范,本身就是一个强烈的信号:CDN 和代理层面的支持,可能会比很多应用框架来得更早。

目前的支持情况大致如下:

  • Go 和 Rust:已经允许发送自定义 method string,所以现在就可以调用 QUERY,无需等待库的更新。例如在 Rust 的 reqwest 库中,可以直接使用 Method::from_bytes(b"QUERY")
  • .NET:已实现双向支持。客户端有 HttpMethod.Query,服务端有 HttpMethods.QueryHttpMethods.IsQuery,从 .NET 10 开始可用。
  • Node.js:从 21.7.222+ 版本开始原生解析 QUERY,Fastify 等框架也可以通过 addHttpMethod 来支持它。
  • Spring Framework:已经有一个开放的 Pull Request,正在等待维护者审查。
  • Nginx 和 Apache:这类反向代理需要进行显式配置,才能正确识别并处理 QUERY 方法。
  • 浏览器:技术上现在就能发送 QUERY。因为 fetch() API 将 HTTP method 当作普通字符串处理,所以 fetch(url, { method: "QUERY" }) 在现有浏览器中是可行的。不过,MDN 目前还未将其文档化,且 QUERY 不在 CORS 安全清单方法之列。这意味着跨域 QUERY 请求每次都会触发一次 preflight OPTIONS 请求,你的服务器必须明确返回 Access-Control-Allow-Methods: QUERY 头,否则请求会失败。

QUERY 方法目前处于 “Proposed Standard”(推荐标准)阶段。这意味着它已不再停留于纯理论文本,而是进入了真实实现开始涌现的阶段。但要让浏览器、服务器、代理、CDN 和框架全面铺开,仍需要时间。现实一点看,真正大范围普及可能要等到 2027 至 2028 年。所以,请不要明天就把所有 POST /search 接口都删掉。现在更合理的做法是,开始理解它、实验它,并为未来的迁移留好位置。

你应该认真思考的两个问题

如果你是那种“代码能跑就行”,但不太关心它为什么能跑的开发者,有两个问题值得你停下来想一想。

第一,你真的理解 idempotent 的含义吗?你知道为什么这个属性能让 GET 被安全地缓存和重试,而 POST 却不能吗?

第二,在这个 RFC 发布之前,你是否已经意识到:GET 方法本来就不该依赖请求 body。那些你在某些客户端库里见过的“给 GET 塞 body”的技巧,一直都是未定义行为,而不是一个官方的隐藏能力?

这两个问题,在面试里经常出现,而它们也正是 QUERY 被设计出来所要解决的核心矛盾。

说到底,QUERY 的出现,不是为了让 HTTP 多一个新玩具。它是在修正一个我们用了很多年、但并不诚实的接口。复杂搜索,终于不用再假装自己是一个 POST 请求了。


如果你想深入了解HTTP、HTTPS、DNS等网络协议栈的底层原理与实战,可以在云栈社区找到更多技术同好的深度讨论与经验分享。




上一篇:7个GitHub硬核项目:把Claude接进你的开发工作流,效率翻倍
下一篇:2026年AI产品栈搭建:值得投入生产的12个开源项目
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-7-12 03:20 , Processed in 0.628191 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表