Web 服务

用 curl 调试 REST API 全流程:方法、头、鉴权、上传与诊断

curl 是多数 API 排障的第一现场,本文从"看清一次请求到底发了什么"讲起,覆盖 GET/POST、Query 与 Body、Bearer/JWT 鉴权、Multipart 上传,并教你把响应时间与状态码一并读出来。

作者:巧匠团队·7 分钟阅读·更新于 2026-09-06

先学会看穿自己:-v 与 -i 揭示一次请求的全貌

调试的开端不是猜,而是"看清楚我到底发出去什么、收到什么"。curl -v 会把发出的请求行、请求头、以及 TLS 握手与响应头全部打印,-i 则直接显示响应头而不止状态码。两者是定位"到底是接口拒绝了我、还是我请求写错"的分水岭。

尤其要盯着几个字段:Content-Type 是否与 body 匹配、Authorization 有没有被正确的头名携带、Host 与地址是否一致。下面这条命令一次看清发收双方。

curl -v https://api.example.com/v1/users -H "Authorization: Bearer $TOKEN"
# 只看响应头
curl -sI https://api.example.com/v1/users
# 若要 -v 的详实又要输出干净,可重定向 body
curl -v -o /dev/null https://api.example.com/v1/users

方法、Query 与 Body:GET 别塞数据,POST 选对 Content-Type

REST 语义下 GET 只拉不写,除非带 Query 参数(放在 ? 之后);POST 的载荷则放 body。写错之后最常见的两类问题:把查询条件硬拼在 POST 路径上导致路由解析失败;或者 POST 了 JSON 却忘了声明 application/json,服务端按表单解析出一堆 undefined。

curl 里 JSON body 用 -H 指定 Content-Type 再 -d 传字符串;--data-urlencode 适合带特殊符号的表单/Qeury 编码。下面是三个请求的对照:GET 查询、POST+JSON、POST+form。

GET
curl -sG https://api.example.com/v1/users --data-urlencode "page=1" --data-urlencode "name=张"
POST JSON
curl -s -X POST https://api.example.com/v1/users \
  -H "Content-Type: application/json" \
  -d '{"name":"alice","active":true}'
POST form
curl -s -X POST https://api.example.com/v1/login \
  -d "username=alice&password=secret"

鉴权三连:Bearer、Basic 与 cookie 会话

现代 API 最常见是 Bearer Token(JWT),curl 里就是 Authorization: Bearer <token> 一个头;Basic 是用户名密码 base64,用 -u user:pass 最省事;需要维持会话的网页接口则用 -c 保存 cookie、-b 携带 cookie。

调试鉴权时最常踩的坑是:token 已过期但你还在用旧值排障,以及 Authorization 头被重复设置导致后端读到脏值。合理做法是先把 token 单独存到一个变量,请求时引用,避免敲错并方便切换。

Bearer
TOKEN=$(curl -s -X POST https://auth.example.com/token -d "grant_type=client_credentials" -H "Authorization: Basic Zm9vOmJhcg==" | jq -r .access_token)
curl -s https://api.example.com/me -H "Authorization: Bearer $TOKEN"
Basic
curl -s -u alice:secret https://api.example.com/private
Cookie
curl -s -c /tmp/ck.txt -d "user=a&pass=b" https://app.example/login
curl -s -b /tmp/ck.txt https://app.example/profile

上传与文件:multipart/form-data 的正确姿势与进度反馈

文件上传的 Content-Type 是 multipart/form-data,curl 用 -F 指明字段名与本地文件路径,curl 会自动生成 boundary 边界并填对 Content-Type,手写边界永远是不必要的自找麻烦。

多字段+多文件的请求把多个 -F 拍平即可;上传大文件想看清进度,用 --progress-bar,想限速防止拖垮内网或公网,用 --limit-rate。还有一个小坑:文件路径里带空格要用引号包住。

单文件
curl -X POST https://api.example.com/files \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@/tmp/report.pdf" -F "note=for review"
多文件带进度与限速
curl --progress-bar --limit-rate 5M \
  -F "a=@a.zip" -F "b=@b.zip" \
  -H "Authorization: Bearer $TOKEN" \
  https://api.example.com/archive
# 只上传字节流而非文件(用分号或 < 重定向)
cat body.json | curl -d @- https://api.example.com/parse

诊断响应:状态码、耗时与重定向一次读出

调试到"请求发出去了但结果不对"时,就看响应本身:-w 能让你一次性拿到 http_code、DNS/tcp/tls/starttransfer/total 多段耗时。mssql:慢在 DNS、慢在手握、慢在 body 传输,对策完全不同。

重定向用 -L 自动跟随,否则 301/302 你会只收到一张空跳转。用 -w 拼接 + -o /dev/null 是标准打法,能把真实接口耗时和前端"慢"这种模糊描述对齐。下面给出一个完整的耗时探针命令。

curl -s -o /dev/null -w "http=%{http_code} \nDNS=%{time_namelookup}s TCP=%{time_connect}s TLS=%{time_appconnect}s TTFB=%{time_starttransfer}s total=%{time_total}s \nredirs=%{num_redirects} url=%{url_effective}\n" \
  -L https://api.example.com/v1/orders
# 只看重定向链 不拉 body
curl -sIL https://api.example.com

错误示范与修复:撞上证书与编码错误时别再猜了

自签名或内网 https 常报 SSL certificate problem: self-signed,正确做法是本地导入 CA 证书或加 --cacert,直接丢 -k 这种关闭校验证书的行为是不推荐的兜底,它会把 MITM 的门也打开。

另一个高频错误是返回乱码——多半是服务端返回 gzip 压缩而你 -d 存成文本:用 --compressed 让 curl 自动解压,否则你会对着乱码排错却找不到根源。把这两类从"靠猜"变成"用对参数"。

# 错误示范:关闭校验证书绕坑
# curl -k https://internal.example /api

# 修复对照:用自家 CA 或临时 cacert
curl --cacert /etc/ssl/certs/ca.pem https://internal.example/api
# 错误示范:不解压存乱码
# curl -s https://api.example/export > out.txt
# 修复:--compressed 自动解压
curl --compressed -s https://api.example/export > out.txt

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。