如何使用 cURL 发送 JSON:-d、--json 及常见错误的完整指南

主要收获:

  • 使用 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/jsonAccept: 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 请求------几乎总是 POSTPUTPATCH------其 请求主体 是一个 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-Typeapplication/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/json
  • Accept: 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_searchscrape_htmlscrape_markdown,浏览器自动化集 browser_* 等 --- 每个工具都通过相同的 POST-a-JSON-body 模式调用,只有 methodparams 变化。

当然,您不必一直用原始 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-TypeAccept,适用于 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-TypeAccept 头都设置为 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-TypeAccept 设置为 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 之后,以使其优先。

相关推荐
半壶清水1 小时前
用python脚本加html自建的书法字典
开发语言·python·html
The moon forgets1 小时前
DreamVLA:世界知识驱动的视觉-语言-动作新范式
人工智能·pytorch·python·深度学习·具身智能·vla
程序猿阿伟1 小时前
《OpenClaw×NVIDIA模型目录实战指南》
人工智能
科技与数码1 小时前
鸿蒙AI防诈能力:场景化防诈+换脸检测+亲情防诈
人工智能·华为·harmonyos
大江东去浪淘尽千古风流人物1 小时前
【OpenCV parallel_for_】并行框架源码深度解析:7种后端调度、线程池自旋等待、工作窃取与跨平台CPU Yield指令全拆解
人工智能·opencv·计算机视觉·多线程·parallel_for_·tbb
code_pgf1 小时前
PointPillars 3D 目标检测详解
人工智能·目标检测·3d
myenjoy_11 小时前
Python + Snap7 实现西门子 S7-1200/1500 数据采集
开发语言·python
泠不丁1 小时前
物联网实时传输可靠性:基于 Zigbee 网络的穿戴设备协议栈调优
人工智能
卡梅德生物科技小能手1 小时前
卡美德生物科普:LINGO-1(神经修复关键负向调控因子)
人工智能·经验分享·深度学习