主要收获:
- 使用 cURL 发送 JSON 是两件事,而不是一件。 你将 JSON 主体附加到请求中,并通过
Content-Type: application/json头告诉服务器这是一种 JSON。如果跳过头,很多 API 会拒绝或者错误解析主体。 -d/--data传递有效负载;头由你负责。 经典模式是curl -X POST -H "Content-Type: application/json" -d '{...}' URL。-d本身并不会设置任何 JSON 头。--json是现代快捷方式。 在 curl 7.82.0 中新增,--json '{...}'发送主体,并通过一个标志同时设置Content-Type: application/json和Accept: application/json。- Shell 引号是大多数人出错的地方。 用单引号包裹 JSON,这样 shell 不会吞掉内部的双引号;在 Windows
cmd中规则不同,使用有效负载文件更安全。 @file从磁盘读取主体------但选择正确的数据标志。-d @body.json删除换行;--data-binary @body.json和--json @body.json按字节发送文件。- 相同的请求形状驱动真实的 API。 向托管 Scrapeless MCP 端点发出的 JSON-RPC 调用只是一个带有 JSON 主体和身份验证头的 POST------这正是本指南教授的确切模式。
- 免费开始。 新的 Scrapeless 账户包括免费爬取浏览器运行时和居民代理访问------请在 Scrapeless 官网注册。
介绍:每个 API 集成开始的请求
几乎所有现代 web API 都支持 JSON。你用 JSON 主体进行身份验证,用 JSON 主体提交作业,在 MCP 服务器上用 JSON 主体调用工具。在这些操作在脚本或 SDK 内部运行之前,通常在终端中以单个 curl 命令开始------确认端点按文档所述行为的最快方式。
问题在于"使用 curl 发送 JSON"隐藏了两个容易混淆的独立要求。一是将 JSON 文本附加为请求主体。另一个是通过 Content-Type 头声明主体 是 JSON,以便服务器正确解析,而不是将其视为表单数据。将主体搞对但忘记头部,严格的 API 会返回 400 或者默默读取无效信息。在 shell 中错误引用 JSON,curl 就会发送一个本来就不是有效 JSON 的扭曲字符串。
本指南明确了"使用 curl 发送 JSON"的确切含义,讲解了执行此操作的两个标志系列(-d 加头部和更新的 --json),展示了可以对公共回显端点运行的示例,并记录了产生混淆错误的常见错误。最后通过将相同的请求形状映射到对 JSON API 的真实调用------托管的 Scrapeless MCP 端点------来结束,以便这一模式能直接从终端传递到生产环境。有关相关背景,请参阅我主页和请在 Scrapeless官网注册博客中关于async HTTP 抓取的 aiohttp 指南 和有关 SSL 代理的解释。
"使用 cURL 发送 JSON"的含义
cURL (围绕 libcurl 的命令行工具)通过 HTTP 和许多其他协议传输数据。"使用 cURL 发送 JSON"意味着发出一个 HTTP 请求------几乎总是 POST、PUT 或 PATCH------其 请求主体 是一个 JSON 文档,Content-Type 头 被设置为 application/json。
这两部分是独立的,且都很重要:
- 主体 是原始 JSON 文本------例如
{"product":"laptop","max_price":1200}。curl 会逐字发送这些字节作为请求实体。 Content-Type头 告诉服务器如何解释这些字节。没有它,curl 的-d默认是application/x-www-form-urlencoded,这是用于 HTML 表单提交的格式。看到该头的 JSON API 可能会拒绝请求或试图(并失败)将主体解析为表单字段。
因此,正确的 JSON 请求总是将 JSON 主体与 JSON 内容类型配对。唯一的问题是你使用哪个 curl 标志来产生这种配对------这就是经典的 -d 加头部的方法与下面介绍的单标志 --json 快捷方式之间的区别。
一个快速的术语说明:-d 是 --data 的简写,-H 是 --header 的简写。它们可以互换使用;本指南在示例中使用简写,并在有帮助的地方命名长形式。
方法 1:-d / --data 搭配 Content-Type 头
这是通用且处处有效的方法,也是 API 文档中最常见的方式。你用 -d 提供主体,用 -H 提供头部:
bashCopy
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"product":"laptop","max_price":1200}'
正在发生三件事:
-X POST设置了 HTTP 方法。严格来说,-d已经暗示了POST,因此此处的-X POST是可选的------但声明它使意图明确,如果你以某种方式切换主体标志导致默认使用GET,则是必需的。-H "Content-Type: application/json"声明了主体格式。-d '{...}'附加了 JSON。单引号防止 shell 解析 JSON 内的双引号。
将此请求发送到 httpbin.org/post --- 一个回显它收到的任何内容的公共端点 --- 返回:
jsonCopy
{
"data": "{\"product\":\"laptop\",\"max_price\":1200}",
"headers": {
"Accept": "*/*",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "curl/8.18.0"
},
"json": {
"max_price": 1200,
"product": "laptop"
},
"origin": "203.0.113.10",
"url": "https://httpbin.org/post"
}
// 字段值是示例;结构是 httpbin 返回的内容。
成功的关键信号是 json 对象:只有在主体解析为有效 JSON 并且 Content-Type 为 application/json 时,httpbin 才会填充它。Accept 头为 */* --- curl 的默认值 --- 因为 -d 不会影响 Accept。请注意,单独使用 -d 并不会设置 任何 JSON 头:上面的 Content-Type 仅因为你添加了 -H 行而存在。去掉该行,httpbin 将报告 Content-Type: application/x-www-form-urlencoded 和一个空的 json 字段。
方法 2:--json 标志(curl 7.82.0+)
--json 标志在 curl 7.82.0(2022 年初发布)中引入,将常见情况简化为一个选项。使用 curl --version 检查你的版本;如果报告 7.82.0 或更新,--json 就可用。
bashCopy
curl -X POST https://httpbin.org/post \
--json '{"product":"laptop","max_price":1200}'
单个 --json 同时完成三项工作。它将提供的文本作为请求主体发送,并为你设置 这两个 头:
Content-Type: application/jsonAccept: application/json
第二个头是与方法 1 的实际区别:--json 还告诉服务器你 希望 返回 JSON,这些 API 用来选择它们的响应格式。通过 httpbin 回显请求确认了这一点:
jsonCopy
{
"data": "{\"product\":\"laptop\",\"max_price\":1200}",
"headers": {
"Accept": "application/json",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "curl/8.18.0"
},
"json": {
"max_price": 1200,
"product": "laptop"
},
"origin": "203.0.113.10",
"url": "https://httpbin.org/post"
}
// 注意现在 Accept 和 Content-Type 都是 application/json。
你可以多次传递 --json,curl 会将片段合并为一个主体------这对于从多个部分组装有效负载很方便。如果需要 覆盖 --json 设置的某个头(比如,其他的 Accept),在其后添加显式的 -H;后定义的头优先。
何时使用每种方法?在当前 curl 上的新作业中使用 --json。当你必须支持旧版 curl 构建时,或者当你想完全控制存在的头时,或者当你遵循的文档以这种方式编写时,使用 -d 加 -H。
| 行为 | -d '{...}' |
-d '{...}' -H "Content-Type: application/json" |
--json '{...}' |
|---|---|---|---|
| 作为主体发送 JSON | 是 | 是 | 是 |
| 默认 HTTP 方法 | POST | POST | POST |
设置 Content-Type: application/json |
否(默认为表单编码) | 是(你设置了它) | 是(自动) |
设置 Accept: application/json |
否 | 否 | 是(自动) |
| 最小 curl 版本 | 任意 | 任意 | 7.82.0 |
使用 @ 发送 JSON 文件
以内联 JSON 的字段数超过几个后就变得笨重,较大的有效负载应该放在文件中。-d 和 --json 都接受 @ 前缀,以从路径读取主体。考虑一个名为 body.json 的文件,如下:
jsonCopy
{
"product": "laptop",
"max_price": 1200
}
你可以使用任一标志发送它:
bashCopy
# 经典:数据标志 + 显式头
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d @body.json
# 现代:一个标志
curl -X POST https://httpbin.org/post \
--json @body.json
读取文件的方式有一个微妙但重要的区别。-d @body.json 从文件中去掉换行符和回车符 --- 这是因为 -d 是为表单数据设计的。到达服务器的主体会变成 { "product": "laptop", "max_price": 1200}:仍然是有效的 JSON(令牌之间的空白是允许的),但不再是与磁盘上的字节逐字相同。
两个标志可以精确保留文件:
bashCopy
# --data-binary 保留每个字节,包括换行符
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
--data-binary @body.json
# --json @file 也按原样发送文件
Copy
curl -X POST https://httpbin.org/post \
--json @body.json
对于普通的 JSON,剥离换行符的版本仍然可以解析,因此 -d @file 通常有效。但是,如果有效负载必须逐字与文件匹配(签名是根据确切的字节计算的,或者文件包含具有意义的嵌入换行符的字符串值),则可以使用 --data-binary @file 或 --json @file。
您还可以通过使用 @- 从标准输入管道传输主体,这在另一个程序生成 JSON 时很方便:
bashCopy
generate_payload | curl -X POST https://httpbin.org/post --json @-
常见错误(及如何避免)
这些错误会把五秒钟的 curl 变成调试会话。
1. 忘记 Content-Type 头部
最常见的错误。使用普通的 -d 而没有头部,curl 会发送 Content-Type: application/x-www-form-urlencoded。然后 JSON API 要么拒绝请求(返回 4xx),要么读取空的主体。解决方案: 添加 -H "Content-Type: application/json",或切换到 --json,它会为您设置。
2. Shell 引号破坏 JSON
JSON 使用双引号;大多数 shell 也用双引号进行插值。将有效负载用双引号包裹会让 shell 在 curl 看见它之前清除或扩展其中的部分内容:
bashCopy
# 错误的示例:shell 消耗了内部的双引号
curl -X POST https://httpbin.org/post --json "{"product":"laptop"}"
# 正确的:用单引号包裹整个有效负载
curl -X POST https://httpbin.org/post --json '{"product":"laptop"}'
解决方案: 在 bash/zsh 上用单引号包裹整个 JSON 文档。如果某个值本身必须包含字面单引号,请转义它,或者将有效负载放入文件中并使用 @file --- 这样完全避免了 shell 引号问题。
3. Windows cmd 引号处理不同
Windows cmd.exe 不将单引号视为引号字符,因此单引号技巧在此失效。您要么使用反斜杠转义每个内部双引号,要么 --- 更可靠的方式 --- 将 JSON 放入文件中并发送 @body.json。PowerShell 有自己的引号规则,其 curl 别名历史上指向 Invoke-WebRequest;显式调用 curl.exe,并优先使用 @file 形式来避免意外。解决方案: 在 Windows 上,使用负载文件 @body.json。
4. 让 -G 将主体变成查询字符串
-G/--get 告诉 curl 将 -d 数据作为查询参数附加到 URL,而不是发送主体。这是 GET 请求的正确工具,但如果您在尝试 POST JSON 时保留它,您的有效负载会默默地转移到 URL 中,主体则为空。解决方案: 不要将 -G 与 JSON 主体结合使用;使用 -X POST(或让 -d/--json 默认转为 POST)。
5. 发送无效的 JSON
curl 不验证主体 --- 它发送您提供的任何文本。尾随逗号、未加引号的键或单引号字符串都是服务器会拒绝的内容,通常会返回不透明的解析错误。解决方案: 在发送之前验证有效负载。使用 JSON 解析器的快速本地检查可以捕获大多数问题:
bashCopy
# 在 curl 运行之前快速检查无效 JSON
echo '{"product":"laptop","max_price":1200}' | python -c "import sys, json; json.load(sys.stdin); print('valid')"
6. 忘记 Accept 当 API 内容协商时
一些 API 会返回 XML 或 HTML,除非您请求 JSON。使用 -d 时,您只设置了 Content-Type,而不是 Accept,因此响应可能不是 JSON,即使您的请求是。解决方案: 添加 -H "Accept: application/json",或使用 --json,它会为您设置 Accept。
示例:调用 JSON API
把这些结合起来,这里是一个真实 JSON API 调用的示例。托管的 Scrapeless MCP 端点通过 HTTP 进行 JSON-RPC 通信 --- 也就是说,它就是您一直在构建的请求:一个带 JSON 主体和身份验证头的 POST。从环境变量中读取 API 密钥,以便它不会出现在您的 shell 历史记录中:
bashCopy
# 主体保存在 init.json 中;密钥来自环境,而不是命令行
curl -X POST "https://api.scrapeless.com/mcp" \
-H "x-api-token: ${SCRAPELESS_API_KEY}" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
--data-binary @init.json
其中 init.json 存储 JSON-RPC 握手:
jsonCopy
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "curl-demo", "version": "1.0" }
}
}
每个概念在本指南中都存在:一个 JSON 正文(这里来自一个文件,使用 --data-binary 原样发送),标记其为 JSON 的 Content-Type: application/json 头,一个命名您将接受的格式的 Accept 头,以及携带凭证的身份验证头。托管的端点大约提供两打工具 --- google_search,scrape_html,scrape_markdown,浏览器自动化集 browser_* 等 --- 每个工具都通过相同的 POST-a-JSON-body 模式调用,只有 method 和 params 变化。
当然,您不必一直用原始 curl 与端点交谈 --- 但首先用 curl 验证端点,然后将经过验证的请求移植到您选择的语言中,是节省时间的工作流程。有关 MCP 服务器的完整工具目录和工作代理提示,请参见我主页 5 个 Scrapeless MCP 用例相关博客。
Scrapeless 的适用性
一旦 curl 命令有效,下一步通常是进行大规模请求 --- 针对使用 JavaScript 渲染内容的网站或屏蔽自动化流量的网站。这就是您刚刚学习的请求结构与管理基础架构相遇的地方。
Scrapeless 提供了一个 反检测云浏览器 --- Scrapeless 爬虫浏览器 --- 和 超过 195 个国家的住宅代理,可通过托管的 MCP 端点、SDK 和 CLI 访问。浏览器在云端渲染 JavaScript 密集型页面并管理指纹,因此您在 curl 中原型化的干净 JSON 请求将返回结构化数据,而不是挑战页面。传输细节 --- 固定住宅出口,保持会话 --- 将为您处理;您这一侧保持简单的"POST 一个 JSON 正文,读取 JSON 回来"的循环。
结论
使用 curl 发送 JSON 的关键在于两个要求共同完成:将 JSON 附加为请求正文,并通过 Content-Type 头声明它为 JSON。经典的方式是 -d '{...}' 加上 -H "Content-Type: application/json";现代的一种标志方式是 --json '{...}',它为您设置 Content-Type 和 Accept,适用于 curl 7.82.0 及更高版本。将大型或已签名的有效负载移入文件并使用 --data-binary @file 或 --json @file 发送,以保留每个字节,在 bash 中单引号包裹内联 JSON 以避免 shell 引号的影响,并在 Windows 上使用有效负载文件。相同的请求 --- 正文加内容类型加身份验证头 --- 精确体现了调用真实 JSON API(如 Scrapeless MCP 端点)的样子,这就是为什么在您终端中有效的 curl 能够顺利移植到生产环境中。
常见问题
问:使用 curl 发送 JSON 的最简单方法是什么?
在当前的 curl(7.82.0 或更新版本)中,curl --json '{"key":"value"}' URL 是最简短的正确形式 --- 它发送请求正文并将 Content-Type 和 Accept 头都设置为 application/json。在旧版 curl 中,使用 curl -X POST -H "Content-Type: application/json" -d '{"key":"value"}' URL。
问:为什么我的 JSON API 说正文缺失或无效,即使我已经发送了?
两种常见原因。要么是您发送了 -d 但没有 Content-Type: application/json 头,因此服务器将其视为表单数据 --- 添加头或使用 --json。要么是您的 shell 扰乱了 JSON,因为它被双引号包裹;使用单引号包裹负载,或者将其移入文件并使用 @file 发送。
问:-d,--data-binary 和 --json 之间有什么区别?
-d(--data)发送正文,对于 @file,会去掉换行;它本身不设置 JSON 头。--data-binary 按照给定的内容精确发送正文,包括换行。--json 按原样发送正文 并 将 Content-Type 和 Accept 设置为 application/json;需要 curl 7.82.0 及以上版本。
问:如何发送 JSON 文件而不是内联文本?
在路径前加 @:curl --json @body.json URL,或 curl -H "Content-Type: application/json" --data-binary @body.json URL。在字节必须完全与文件匹配的情况下,优先使用 --json @file 或 --data-binary @file,因为 -d @file 会去掉换行。
问:如何在 Windows 上使用 curl 发送 JSON?
cmd.exe 不支持单引号,因此最简单可靠的方式是将 JSON 存放在文件中并使用 @body.json 发送。如果必须内联,使用反斜杠转义每个内部双引号。在 PowerShell 中,显式调用 curl.exe,以免触发 Invoke-WebRequest 别名,并仍然优先使用 @file 形式。
问:如果我使用 --json,我需要设置 Content-Type 头吗?
不需要。--json 会自动设置 Content-Type: application/json,以及 Accept: application/json。您只需添加一个显式头部来覆盖其中一个---例如一个不同的 Accept --- 在这种情况下,将 -H 放在 --json 之后,以使其优先。