很多 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 之间硬拧。但标准立起来,只是第一步。江湖规矩要改,还得看工具链和中间件认不认。
