使用 curl 调用 REST API:GET/POST/PUT/PATCH/DELETE 与认证实战

迟早你会需要在不编写任何应用程序代码的情况下与一个 API 通信。你可能想查看某个端点返回了什么、复现别人报告的 bug,或在把令牌写进脚本之前先测试它。curl 是从终端完成这项工作的最快方式。

REST API 通过 HTTP 暴露资源,通常返回 JSON。REST 代表表述性状态转移(representational state transfer),是一种基于一组 web 服务约束构建的架构风格。你使用标准 HTTP 方法来创建、查看、更新或删除这些资源。

每个 API 请求由四个部分组成:

• 端点(Endpoint):客户端用于与服务器通信的 URL。 • HTTP 方法:告诉服务器客户端想要执行的操作。最常用的方法是 GET、POST、PUT、PATCH 和 DELETE。 • 请求头(Headers):在客户端与服务器之间传递额外信息,例如认证令牌和内容类型。 • 主体(Body):发送给服务器的数据。

本指南介绍如何使用 curl 与 RESTful API 交互。curl 是一个用于从远程服务器传输数据或向远程服务器传输数据的命令行工具。它在 macOS 和大多数 Linux 发行版中默认安装。

curl 选项

curl 命令的语法是:

curl [options] [URL...]

本指南中用到的选项有:

• -X, --request - 要使用的 HTTP 方法。 • -i, --include - 在输出中包含响应头。 • -sS, --silent --show-error - 隐藏进度输出但仍显示错误。 • --fail-with-body - 在 HTTP 错误时返回非零退出码,同时仍打印响应主体。 • -d, --data - 随请求发送的数据。 • -G, --get - 在 GET 请求中将 -d 或 --data-urlencode 数据作为查询参数发送。 • --data-urlencode - 发送前对数据进行 URL 编码。 • -H, --header - 要发送的额外请求头。 • -u, --user - Basic 认证的用户名和密码。 • -w, --write-out - 传输完成后打印传输详情,例如状态码。

示例使用的是 JSONPlaceholder,一个用于测试的免费虚拟 API。它接受每个写入请求并返回逼真的响应,但服务器上实际不会保存任何内容,因此你可以随意多次运行每条命令。

HTTP GET

GET 方法向服务器请求一个资源。在使用 curl 发起 HTTP 请求时,GET 是默认方法,因此无需 -X。

以下示例返回所有文章的 JSON 列表:

curl https://jsonplaceholder.typicode.com/posts

要筛选结果,请向 URL 追加查询参数。请将 URL 用引号包裹,因为 shell 会把 ? 和 & 视为特殊字符。在 macOS 的默认 shell zsh 中,未加引号的 ? 会失败并报 no matches found:

curl 
"https://jsonplaceholder.typicode.com/posts?userId=1"
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita..."
  },
  ...
]

服务器只返回属于用户 1 的文章。

当参数值包含空格或其他特殊字符时,让 curl 为你构建查询字符串。-G 选项将 --data-urlencode 数据附加到 URL,而不是作为主体发送:

curl -G --data-urlencode 
"title=qui est esse"
 
\

    https://jsonplaceholder.typicode.com/posts

curl 以 GET /posts?title=qui+est+esse 发送请求,API 返回具有该标题的单篇文章。

许多 API 可以用多种格式响应。要显式请求 JSON,请设置 Accept 请求头:

curl -H 
"Accept: application/json"
 https://jsonplaceholder.typicode.com/posts/1

HTTP POST

POST 方法向服务器发送数据以创建新资源。

以下命令使用 -d 选项发送带有表单编码数据的 POST 请求:

curl -X POST -d 
"userId=5&title=Hello World&body=Post body."
 
\

    https://jsonplaceholder.typicode.com/posts
{
  "userId": "5",
  "title": "Hello World",
  "body": "Post body.",
  "id": 101
}

API 创建了一个新文章并为其分配了 ID 101。注意 userId 以字符串 "5" 的形式返回。表单数据没有类型,因此每个值都作为文本到达。

默认情况下,当使用 -d 但未使用 Content-Type 请求头时,curl 会发送 Content-Type: application/x-www-form-urlencoded。严格来说,-X POST 在这里是多余的,因为 -d 已经将方法切换为 POST,但许多人为了可读性仍保留它。

要发送 JSON 数据,请将 Content-Type 请求头设为 application/json:

curl -X POST -H 
"Content-Type: application/json"
 
\

    -d 
'{"userId": 5, "title": "Hello World", "body": "Post body."}'
 
\

    https://jsonplaceholder.typicode.com/posts
{
  "userId": 5,
  "title": "Hello World",
  "body": "Post body.",
  "id": 101
}

这一次 userId 是一个数字,因为服务器将主体解析为 JSON。大多数 REST API 期望 JSON,因此这是你最常用到的形式。

更多示例(包括 --json 简写),请参阅如何使用 curl 发起 POST 请求。

HTTP PUT

PUT 方法用请求主体中提供的数据替换服务器上的资源。请发送完整的资源,因为许多 API 会将对被省略字段的重置为空值或默认值。

以下命令替换 ID 为 5 的文章:

curl -X PUT -H 
"Content-Type: application/json"
 
\

    -d 
'{"userId": 5, "title": "Hello World", "body": "Post body."}'
 
\

    https://jsonplaceholder.typicode.com/posts/5
{
  "userId": 5,
  "title": "Hello World",
  "body": "Post body.",
  "id": 5
}

响应包含我们发送的确切字段,以及来自 URL 的 id。

HTTP PATCH

PATCH 方法对资源应用部分更新。与 PUT 不同,它只更新请求主体中包含的字段,而保留所有其他字段不变。

要仅更改文章 5 的标题,只发送该字段:

curl -X PATCH -H 
"Content-Type: application/json"
 
\

    -d 
'{"title": "Hello Universe"}'
 
\

    https://jsonplaceholder.typicode.com/posts/5
{
  "userId": 1,
  "id": 5,
  "title": "Hello Universe",
  "body": "repudiandae veniam quaerat sunt sed..."
}

标题发生了变化,而 userId 和 body 保留了它们的原始值。

HTTP DELETE

DELETE 方法从服务器移除指定的资源。没有确认提示,因此在针对真实 API 运行前请仔细核对 URL:

curl -X DELETE https://jsonplaceholder.typicode.com/posts/5

JSONPlaceholder 返回一个空的 JSON 对象({})和 200 状态码。其他 API 常回复 204 No Content,表示成功但没有响应主体。仅凭空主体不足以判定成功,因此请检查状态码以确认。

认证

大多数 API 需要认证。curl 支持几种常见的认证方法。

Bearer 令牌

Bearer 令牌认证由 OAuth 2.0 API 使用。在 Authorization 请求头中传递令牌:

curl -H 
"Authorization: Bearer {ACCESS_TOKEN}"
 https://api.example.com/data

Basic 认证

对于使用 HTTP Basic 认证的 API,请用 -u 选项传递用户名和密码:

curl -u username:password https://api.example.com/data

curl 会对凭据进行编码并自动在 Authorization 请求头中发送。如果你只传入用户名(-u username),curl 会提示输入密码,从而避免其进入你的 shell 历史记录。

API 密钥

某些 API 接受将 API 密钥作为自定义请求头:

curl -H 
"X-API-Key: {API_KEY}"
 https://api.example.com/data

其他 API 期望将密钥作为查询参数:

curl 
"https://api.example.com/data?api_key={API_KEY}"

检查响应

使用 -i 标志在输出中包含 HTTP 响应状态行和响应头:

curl -i https://jsonplaceholder.typicode.com/posts/1
HTTP/2 200
content-type: application/json; charset=utf-8
...

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere...",
  "body": "quia et suscipit..."
}

第一行显示协议和状态码,随后是响应头、一个空行以及主体。

要美化打印 JSON 响应,请将输出通过管道传给 python3:

curl -s https://jsonplaceholder.typicode.com/posts/1 
|
 python3 -m json.tool

如果你安装了 jq,它会提供带颜色高亮的格式化输出:

curl -s https://jsonplaceholder.typicode.com/posts/1 
|
 jq

检查状态码

在脚本中,你通常只关心请求是否成功。-w 选项在传输完成后打印所选详情,%{http_code} 保存响应状态:

curl -s -o /dev/null -w 
'%{http_code}\n'
 https://jsonplaceholder.typicode.com/posts/1
200

这里 -s 隐藏进度条,-o /dev/null 丢弃主体,因此唯一打印的是状态码。请求一个不存在的文章,例如 /posts/9999,同样的命令会打印 404。

默认情况下,即使服务器返回 404 或 500 等错误,curl 也会以状态 0 退出。对于自动化脚本和 CI 检查,请添加 --fail-with-body 使这些失败显式化:

curl -sS --fail-with-body https://jsonplaceholder.typicode.com/posts/9999
curl: (22) The requested URL returned error: 404
{}

curl 现在以代码 22 退出,并仍会打印错误主体,这通常会说明出错原因。--fail-with-body 选项需要 curl 7.76.0 或更高版本。在较旧的系统(如 RHEL 8)上,请改用 --fail,它的失败方式相同但会丢弃主体。

当你需要查看一切,包括 curl 发送的请求头时,使用 -v 获取详细输出。

快速参考

关于可打印的快速参考,请参阅 curl 速查表。

命令说明
curl URLGET 请求
curl -G --data-urlencode "key=a b" URL带编码查询参数的 GET 请求
curl -X POST -d "data" URL使用表单数据的 POST 请求
curl -X POST -H "Content-Type: application/json" -d '{}' URL使用 JSON 主体的 POST 请求
curl -X PUT -H "Content-Type: application/json" -d '{}' URLPUT(完整替换)
curl -X PATCH -H "Content-Type: application/json" -d '{}' URLPATCH(部分更新)
curl -X DELETE URLDELETE 请求
curl -H "Authorization: Bearer TOKEN" URLBearer 令牌认证
curl -u user:pass URLBasic 认证
curl -i URL包含响应头
curl -s -o /dev/null -w '%{http_code}\n' URL仅打印状态码
curl -sS --fail-with-body URL在 HTTP 错误时失败并打印错误主体
curl -s URL | python3 -m json.tool美化打印 JSON 响应

故障排查

SSL certificate problem: unable to get local issuer certificate 服务器正在使用自签名或不受信任的证书。在你信任该主机的受控测试环境中,添加 -k 以跳过证书验证。不要对不受信任或生产服务器使用 -k。

401 Unauthorized 或 403 Forbidden 你的认证凭据缺失、不正确或已过期。验证令牌或 API 密钥,并查阅 API 文档确认正确的请求头名称与格式。

Connection refused 服务器在指定的主机和端口上不可达。确认端点 URL 正确且服务器正在运行。

Response is not valid JSON 服务器可能正在返回 HTML 格式的错误页面。使用 -i 检查包含状态码和请求头的完整响应以诊断问题。

zsh: no matches found URL 包含 ? 或 * 且未加引号,因此 zsh 尝试将其作为文件名模式展开。请将 URL 用双引号包裹。

curl: (6) Could not resolve host 无法解析主机名。检查 URL 是否有拼写错误,并确认主机可达。

常见问题

PUT 与 PATCH 有什么区别?PUT 用请求主体中的数据替换整个资源。请发送完整的资源,因为 API 决定了被省略字段的处理方式,而许多 API 会将它们重置为空值或默认值。PATCH 应用部分更新。当你想更改特定字段而不替换整个资源时,请使用 PATCH。

我是否总需要指定 -X GET? 不需要。GET 是 curl 中的默认方法。像 -d 这样的选项会自动选择 POST。使用 -X 显式选择 PUT、PATCH 或 DELETE 等方法。

如何发送多个请求头?为每个请求头重复 -H 标志:

curl -H 
"Authorization: Bearer TOKEN"
 -H 
"Accept: application/json"
 URL

如何将 API 响应保存到文件?使用 -o 标志将输出写入文件:

curl -o response.json https://jsonplaceholder.typicode.com/posts/1

结论

curl 处理与使用 REST API 相关的每一步,从快速的 GET 到带认证的 PATCH。当请求表现不如预期时,从 -i 或 -w '%{http_code}' 开始,查看服务器实际返回了什么。

更多 curl 示例和选项,请参阅 Linux 中的 curl 命令指南。