很多 API 团队都遇到过同一个尴尬:查询本来是只读的,但条件一复杂,GET URL 变得又长又脆;换成 POST,body 是舒服了,语义又像在提交或触发什么。

RFC 10008 正式定义了新的 HTTP QUERY 方法,就是为这个缝隙补标准语义。我的判断很简单:QUERY 有用,但现在不是迁移口号。它更像一把专门的工具,适合复杂只读查询,不适合把 GET 和 POST 一锅端。

GET 和 POST 的问题,不只是能不能放参数

GET 仍然适合简单过滤。比如状态、角色、排序、页码,这类参数放在 URL 里很清楚,也方便分享、收藏和调试。

问题出在复杂查询。嵌套条件、数组、关系过滤、特殊字符一多,URL 会快速变长。编码也会变麻烦。更现实的一点是,查询参数经常进入访问日志、网关日志和监控系统,敏感条件暴露面会变大。

GET 理论上可以带请求体,HTTP 标准并没有禁止。但这条路不该依赖。客户端、代理、防火墙、服务器对 GET body 的处理并不一致:有的转发,有的忽略,有的直接拒绝。

POST 能稳定承载 body,却有另一层错位。行业惯例和很多中间件默认把 POST 当成可能改变状态的请求。拿它做只读查询,会影响重试、缓存、审计和网关策略。

方法适合什么主要风险现实判断
GET简单过滤、可分享链接URL 过长、编码复杂、参数易进日志仍是默认选择
GET + body理论上的复杂只读查询生态处理不一致不应依赖
POST提交、创建、触发处理,也常被拿来做复杂查询只读语义不清,不利于缓存和自动重试常见但不理想
QUERY带请求体的复杂只读查询客户端、网关、缓存、工具链支持有限可试点,不宜全量迁移

QUERY 的价值就在这里:它把“带 body”和“只读查询”放进同一个方法语义里。不是发明了新能力,而是补了一笔长期欠账。

QUERY 的关键,是安全、幂等和缓存键

RFC 10008 对 QUERY 的定位接近“带请求体的 GET”。它被设计为安全且幂等。正确实现时,多次执行同一个 QUERY 请求,不应改变服务端状态。

这对平台系统很重要。重试策略、限流规则、审计分类、缓存判断、网关路由,很多都依赖 HTTP 方法语义。方法说不清,中间件只能猜。

但“可缓存”不是“天然按 URL 缓存”。QUERY 的查询条件在请求体里,缓存系统必须把请求体纳入缓存键。否则同一个 URL、不同 body 的两个请求,可能命中同一份结果。

那不是性能问题,是正确性问题。

对后端/API 设计者来说,采用 QUERY 前至少要做三件事:

  • 把接口限定为真正只读,不要在查询里顺手写审计状态、刷新计数或触发异步任务。
  • 明确请求体结构,避免把任意 DSL 直接暴露给外部调用方。
  • 写清楚重试和缓存语义,尤其是 body 如何参与缓存键。

对平台与网关中间件团队来说,重点不在业务代码,而在链路:

  • 客户端 SDK、测试工具、OpenAPI 描述能否识别 QUERY。
  • 反向代理、WAF、服务网格、API Gateway 是否放行未知方法。
  • 日志、APM、审计系统能否正确记录方法、body 摘要和缓存命中信息。
  • 缓存层是否支持把请求体哈希纳入 key,而不是只看 URL。

这类团队最现实的动作不是“立刻迁移”,而是先做兼容性验证。选一条内部复杂查询链路,打通客户端、网关、服务端和缓存,再决定是否扩大范围。

适合试点,不适合替代所有搜索接口

QUERY 最适合的场景,是受控环境里的复杂只读查询。比如内部报表筛选、权限条件组合、搜索 DSL、跨资源关系查询。这些接口通常不需要用户复制 URL 分享,客户端和网关也更可控。

开放 API 要更保守。很多企业代理、防火墙、老旧 SDK 对未知 HTTP 方法并不友好。浏览器表单长期原生支持的也主要是 GET 和 POST。

如果一个筛选页需要分享、收藏、复制链接,GET 仍然更合适。URL 本身就是产品体验的一部分,不能为了语义纯度把用户操作变复杂。

接下来真正要看三件事,不是口号:

变量为什么重要影响
浏览器与 Fetch 生态决定前端调用是否顺滑影响公开 Web API 采用速度
Nginx、Envoy、Cloudflare、API Gateway 等默认策略决定链路是否会拦截或降级影响生产可用性
OpenAPI、SDK 生成器、缓存产品支持决定工程成本影响团队是否愿意规模化使用

所以,QUERY 现在最合理的位置,是复杂只读查询的新增选项。GET 继续承担简单、可分享、可收藏的查询。POST 继续处理提交和副作用明确的动作。

回到开头那个尴尬:复杂查询终于不用在 GET 和 POST 之间硬拧。但标准立起来,只是第一步。江湖规矩要改,还得看工具链和中间件认不认。