全新 HTTP QUERY 方法详解

原文作者:Manuel (Kreya)
原文链接:https://kreya.app/blog/new-http-query-method-explained/
发布时间:2026 年 6 月 17 日

在 RESTful API 的世界里,我们长期遵循着一套严格的(自我施加的)规则。无论是用 GET 获取数据、用 POST 创建实体,还是用 PUT 更新资源,HTTP 方法都在告诉服务器你的意图是什么。

就在最近,RFC 10008 正式发布,定义了全新的 HTTP QUERY 方法。既然我们已经有了其他 HTTP 方法,为什么还需要这个新方法?让我们一探究竟。

从纯技术角度来看,HTTP 方法只是一个字符串。理论上,你不用发:

GET /api/v1/users
  

也可以发:

FETCH /api/v1/users
  

但在实践中,围绕着众所周知的 HTTP 方法(如 GET 和 POST),存在着大量 RFC 以及隐性的、未文档化的行为。

例如,浏览器在你输入地址或点击书签时会发送 GET 请求。标准的 HTML 表单只允许 GET 和 POST 作为方法。大多数代理、防火墙和 Web 服务器只允许"标准"的 HTTP 方法。

那么,既然我们已经有了一套沿用了几十年且运行良好的方法,为什么还要引入新的 HTTP 方法呢?


使用 GET 进行查询

传统上,如果你想过滤资源,会在 GET 请求中使用查询参数(例如 /api/v1/users?role=admin&status=active&sort=desc)。这对于简单的过滤来说效果很好。但是,当你需要执行复杂的关系查询、深层嵌套或高级逻辑时,URL 会变得无比庞大、难以阅读,有时还会触达浏览器或服务器的字符限制。

其他潜在问题还包括:

  • 发送非 ASCII 或特殊字符作为参数需要编码,会增加请求大小
  • 服务器和其他中间件可能会记录请求参数,在某些情况下可能有问题
  • 表达某些数据结构(如数组)没有明确定义,依赖具体实现(例如 ?roles[0]=admin&roles[1]=reporter vs ?roles=admin&roles=reporter vs ?roles[]=admin&roles[]=reporter
  • 深层嵌套结构的表达同样如此

既然这些都是把数据作为查询参数发送的缺点,那为什么不直接发送一个带 JSON 请求体的 GET 请求呢?再说一次,从理论角度来看,这应该可行。没有任何 HTTP RFC 明确禁止在执行 HTTP GET 请求时使用请求体,但它们指出不应该这样做

结果就是,各种客户端、代理和 Web 服务器对带请求体的 GET 请求处理方式各不相同。有些直接拒绝,有些干脆丢弃请求体,还有些会正常解析它。

因此,使用带请求体的 HTTP GET 是个坏主意——比如,公司防火墙后面的用户或使用不同浏览器的用户可能无法使用你的网站。这也是为什么没有新的 RFC 规定 GET 请求现在应该支持请求体,因为那会破坏大量现有实现。


变通方案:使用 POST 进行查询

既然用 GET 发送请求体会带来问题,变通方案就是用 POST。

虽然 POST 允许携带请求体,但它引入了严重的语义问题。POST 被定义为非幂等的,并且旨在用于资源创建或处理。

虽然这听起来可能不是什么大问题,但在实现诸如失败自动重试之类的功能时会很烦人。由于 GET 方法被定义为安全且幂等的,只要服务器实现正确,我们就可以重试失败的请求而不用担心副作用。

这也使得代理或其他中间件无法自动理解该操作是只读的。例如,中间件可能会自动缓存 GET 请求一段时间,但这对 POST 请求不起作用。


QUERY 方法

以上所有原因,经过多年讨论,最终催生了 QUERY 方法的规范。QUERY 方法没什么特别的,RFC 大致说明它类似于 GET 方法,但带有请求体。它被设计为安全的幂等的

QUERY 请求可以被缓存,但实现必须注意将请求内容纳入缓存键的计算。总而言之,它终于为复杂的搜索查询提供了一个合适的 HTTP 方法。

Kreya HTTP QUERY 示例


QUERY 的注意事项

你可能很想立刻把所有与搜索相关的端点都切换为使用 QUERY。在这么做之前,有几件事你需要考虑:

  1. HTTP QUERY 的支持仍然非常有限,而且可能会持续一段时间。要让它在各处得到完全支持可能需要数年时间。例如,Kreya 在最近的 1.20 版本中增加了对 HTTP QUERY 的开箱即用支持(尽管在此之前已经可以发送自定义 HTTP 方法了)。其他客户端、代理和 Web 服务器可能仍然会拒绝它。

  2. 使用 URL 参数的标准 GET 查询仍然完全没问题。如果没有迫切需要将它们改为 QUERY 方法,那就保持原样。

  3. 如果你的用户应该能够分享或收藏过滤后数据的链接,请继续使用 GET 请求。将链接作为 QUERY 请求分享是行不通的。

  4. 为 QUERY 请求实现自定义缓存比 GET 请求更困难,因为你需要考虑请求体。


结论

简而言之,HTTP QUERY 替代了 POST 来处理只读请求。要让它在各处得到完全支持可能还需要一些时间,但如果普通的 GET 请求不足以满足你的用例,你仍然应该考虑(并测试!)它。