1. 概述
本文将探讨为什么HTTP GET请求不应该包含请求体。首先,我们分析这种请求的常见使用场景,接着说明其潜在问题,最后提供几种替代方案。
2. 为什么有些GET请求会带有请求体?
在开发REST API时,我们可能会遇到需要在GET请求中附加数据的情况。这通常发生在以下场景:
- 执行复杂搜索(如Elasticsearch的搜索API)
- URI过长导致服务器无法处理
这种做法源于对早期HTTP/1.1规范的解读,后来因便利性而流行起来。
2.1. HTTP/1.1规范不明确
RFC 7231规范对GET请求体的描述存在歧义:
GET请求消息中的载荷没有明确定义的语义。
这句话引发了多种解读:
- ✅ 语义由各实现者自行决定
- ❌ 这种请求根本不存在语义
结果导致:
- 部分HTTP服务器允许带请求体的GET
- 浏览器、负载均衡器、缓存代理等中间件则不支持
2.2. 便利性驱动的选择
开发者选择带请求体的GET主要出于三个原因:
编码便利性
JSON比复杂查询字符串更结构化,且所有编程语言都支持。Elasticsearch的搜索API就采用了这种设计:相比晦涩的查询字符串,请求体搜索允许我们使用查询领域特定语言(query DSL)编写查询。
可控环境
当客户端和服务器都在自己掌控中时,直接在请求体传递数据更可靠。语义直观性
用带请求体的GET获取数据比用POST更符合直觉。毕竟POST通常用于创建资源。Elasticsearch作者就明确表示:我们更倾向于用GET进行搜索,因为它比POST更能准确描述“获取信息”这个动作。但由于带请求体的GET未被广泛支持,我们的搜索API也接受POST请求。
3. 为什么在GET请求中使用请求体是个坏主意?
虽然带请求体的GET在可读性和开发者体验(DX)上更优,但实际开发中应极力避免这种做法。
3.1. RFC 9110规范合规性
HTTP规范演进明确反对这种做法:
RFC 7231(2014年)警告:
GET请求中的载荷没有明确定义语义;发送载荷可能导致某些现有实现拒绝请求。
RFC 9110(2019年)进一步收紧:
客户端不应在GET请求中生成内容,除非直接向源服务器发送且该服务器已明确表示支持此类请求。源服务器不应依赖私有协议接收内容,因为HTTP通信参与者通常不了解请求链中的中间件。
⚠️ 这彻底解决了HTTP/1.1的歧义问题。为保障跨系统兼容性,避免在GET中使用请求体是明智之选。
3.2. 请求体被忽略
HTTP/1.1最初建议忽略GET请求体:
如果请求方法未为实体主体定义语义,则处理请求时应忽略消息主体。
部分旧服务器会直接丢弃请求体,导致功能异常。
3.3. 请求被拒绝
许多HTTP实现会直接拒绝带请求体的GET请求:
- 浏览器的fetch API会抛出错误
- Spring框架会抛出
HttpMessageNotReadableException
3.4. 请求无法被缓存
RFC 9110规定GET请求应可缓存,但带请求体的GET会破坏这一特性:
- 代理服务器不会读取请求体
- 导致缓存失效
- 最终因多次服务器调用引发性能问题
4. 带请求体的GET请求有哪些替代方案?
4.1. HTTP POST
最简单的替代方案是使用POST:
- POST是限制最少的HTTP方法
- ✅ 总是安全的选择
- 当语义符合时,应优先使用更具体的方法
当你试图给GET添加请求体时,改用POST总是没错的。—— Roy Fielding(HTTP规范作者之一)
4.2. HTTP QUERY
QUERY方法是一个新兴提案,专门用于:
- 安全、可缓存、幂等的带内容请求
- 接受包含查询操作的载荷
示例请求:
QUERY /contacts HTTP/1.1
Host: example.org
Content-Type: example/query
Accept: text/csv
select surname, givenname, email limit 10
示例响应:
HTTP/1.1 200 OK
Content-Type: text/csv
surname, givenname, email
Smith, John, john.smith@example.com
Jones, Sally, sally.jones@example.com
Dubois, Camille, camille.dubois@example.com
⚠️ 目前仍是草案阶段,尚未广泛实现。
4.3. HTTP SEARCH
非标准的SEARCH方法,支持JSON/XML请求体:
SEARCH /search HTTP/1.1
Host: www.example.com
Content-Type: application/json
{
"query": "baeldung",
"sort": "date",
"filters": {
"type": "article",
"category": "tech"
}
}
特点:
- 服务器可解析请求体中的所有字段
- 目前仅用于WebDAV规范
- 未被广泛采用
5. 结论
本文系统分析了带请求体的GET请求的来龙去脉:
- 起源于HTTP/1.1规范的模糊表述
- 因便利性而被部分项目采用
- 但会引发兼容性、缓存、中间件支持等问题
- 当前最佳实践是使用POST替代
- 未来可能有QUERY等标准化方案
始终遵循最新的HTTP规范是保障系统互操作性的关键。当遇到复杂查询需求时,简单粗暴地改用POST才是明智之选。