接口联调卡住的时候,最难受的是分不清问题出在前端还是后端。浏览器里点一下,Network 面板一堆请求混在一起,想改个请求头还得回代码里改完重新构建。这种时候一条 curl 命令就够了,参数写在一行里,想改哪个字段改哪个字段,结果直接打在终端上。
这篇把我平时调接口用得最多的那几个 curl 参数梳理一遍。从 -H、-d、-X 这些基础的,到用 --resolve 绕开 DNS 直连某台机器,再到用 -w 打印各阶段耗时定位慢请求。HTTPS 证书报错的时候,curl 也是把问题缩小到具体哪一层的最快工具。
在本篇文章中,我们将从浅入深,和大家一起学习以下知识:
- 什么场景下 curl 比 Postman 更顺手
- 常用参数速查,长短两种写法的对应关系
- 设置请求头、传参数、带 cookie、基本认证的写法
- POST 请求在 http 和 https 两种协议下的差别
- HTTPS 证书报错怎么用 curl 一层层定位
- 用
--resolve跳过 DNS,直接打某台后端机器 - 用
-w打印 DNS、握手、首字节各阶段耗时,排查慢请求 - 几个容易写错的地方,比如
-d会隐式改方法、单双引号在 shell 里的坑
# 一、什么时候我会敲 curl 而不是开 Postman
平时打交道的接口大致分两类。
一类是自己写的、服务于自己系统的接口。这类接口本地就能跑,参数结构自己最清楚,用 Postman 这种 GUI 工具存一套 collection,改改字段点一下发送,效率是高的。
另一类是别人提供的能力接口,你的系统去调它。这类接口往往鉴权复杂,签名、时间戳、自定义 header 一堆,而且经常只能在特定的机器上才能访问到,比如只有内网某台跳板机能通。这时候 GUI 工具就用不上了,你只有一个 ssh 终端,curl 是唯一的选择。
我自己的习惯是,只要涉及「在服务器上验证」「问题不确定出在哪一层」这两种情况,一律用 curl。原因是它的输出可以被完整贴给后端同事,对方复制过去就能复现,不需要你截图描述半天。
还有一点很实际,curl 命令是纯文本,能直接写进部署脚本和健康检查里。你调通的那条命令,稍微改改就是线上的探活脚本。
# 二、常用参数速查
curl 的参数几乎都有长短两种写法,功能完全一样。
-X/--request [GET|POST|PUT|DELETE|…]使用指定的 HTTP method 发出请求-H/--header设定 request 里的 header-i/--include在输出里带上 response 的 header-d/--data设定 HTTP 请求体参数-v/--verbose输出比较多的过程信息-u/--user使用者账号密码-b/--cookie使用 cookie,可以给字符串也可以给文件路径
所以 curl -X POST http://www.example.com 和 curl --request POST http://www.example.com/ 是完全相同的两条命令,写哪个看你自己习惯。我在终端里手敲用短参数,写进脚本里用长参数,因为长参数半年后回来看还认得出是干什么的。
这里要纠正一个我早年一直搞错的点:-v 不是「显示版本信息」,它是 verbose 的缩写,作用是把整个请求过程打出来,包括 DNS 解析、TCP 连接、TLS 握手、发出去的完整请求头、收到的完整响应头。显示版本用的是 -V(大写)或者 --version。这两个我混过好几次。
-v 的输出里,行首符号是有约定的。* 开头是 curl 自己的过程说明,> 开头是发出去的内容,< 开头是收到的内容。看懂这三个符号,排查问题的效率能翻一倍。
# 三、发请求的几种常见写法
# 3.1 设置 header
curl -i -H "Content-Type: application/json" http://www.baidu.com
-H 可以重复写多次,每个 header 一个 -H。加了 -i 之后响应头会跟着 body 一起打出来,调试的时候基本是默认要加的,不然你看不到状态码,也看不到 Set-Cookie 和缓存相关的头。
如果只想看响应头不想要 body,用 -I(大写 i),它发的是 HEAD 请求。检查一个静态资源的缓存策略、CDN 有没有命中,用 -I 最快。
# 3.2 设置请求参数
curl -X POST -d "param1=value1¶m2=value2" http://www.example.com/
或者拆开写,效果一样,curl 会自动用 & 把它们拼起来。
curl -X POST -d "param1=value1" -d "param2=value2" http://www.example.com/
这里有个坑要注意,键值之间是等号不是冒号。表单编码的格式就是 key=value,写成 param1:value1 的话,整串会被当成一个没有等号的字段名发出去,后端解析不到,你会看到一个「参数为空」的报错,然后在参数值上找半天。
还有一点,只要写了 -d,curl 就会自动把方法切成 POST,并且默认带上 Content-Type: application/x-www-form-urlencoded。所以上面那条命令里的 -X POST 其实可以省掉。但我一般还是会写上,因为显式写出来别人一眼就知道这是个 POST。
参数值里如果有中文、空格或者 & 这类特殊字符,用 --data-urlencode 代替 -d,它会帮你做 URL 编码。GET 请求想把参数拼到 query string 上,加个 -G,curl 就会把 -d 的内容挪到 URL 后面而不是放进 body。
# 3.3 session 认证
curl -X GET 'http://www.baidu.com/' --header 'sessionid:sessionid值'
不少内部系统的鉴权就是这么做的,一个自定义 header 带上会话标识。要注意的是自定义 header 的名字大小写不敏感,但值是敏感的,从浏览器里复制的时候别把前后空格带进来。
# 3.4 使用 cookie
curl -i --header "Content-Type:application/json" -X GET -b ~/cookie.txt http://www.baidu.com
-b 既可以直接给字符串(-b "name=value; name2=value2"),也可以给一个 cookie 文件的路径。配套的还有 -c,作用是把服务端下发的 cookie 存到文件里。这两个搭配起来就能模拟一次完整的登录流程:先用 -c cookie.txt 请求登录接口把会话存下来,后面每次请求都用 -b cookie.txt 带上。
测试文件上传接口的时候用 -F "file=@__FILE_PATH__" 这种写法,注意路径前面那个 @ 不能少,少了的话 curl 会把这串路径当成普通字符串发出去,而不是读文件内容。想看到详细的请求信息就再加个 -v。
curl -i -X POST -F 'file=@/User/uploadFile.txt' -H "token:abc123" -v http://www.example.com/upload
# 3.5 HTTP 基本认证
curl -i -u username:password http://www.baidu.com/api/foo
-u 会把账号密码做 base64 编码放进 Authorization: Basic 头里。这里提醒一句,基本认证的编码不是加密,抓包能直接还原出明文密码,所以走 HTTP 明文协议的基本认证等于没有保护。真要用,前面必须是 HTTPS。
另外密码写在命令行里会进 shell 的 history 文件,机器上别人能翻到。省事的做法是只写 -u username,curl 会交互式地问你要密码,不落盘。
# 四、POST 请求在两种协议下的调法
平时遇到的接口大多是 POST,所以单独把这块拿出来说。
# 4.1 http 协议
curl -v -X POST http://localhost:3000/api/posts --data '{"title":"controller", "content": "what is controller"}' -H 'Content-Type:application/json; charset=UTF-8'
拆开看这几个参数在干什么。
-v 打印完整交互过程。-H(等同 --header)指定请求头,很多服务端会靠请求头做权限校验,比如约定必须带某个 token 头,带了才放行,不带就直接拒绝。--data(等同 -d)是请求体。-X 显式指定请求方法。
这里有个必须注意的配合关系:发 JSON 的时候,Content-Type 一定要写成 application/json。前面说过 -d 默认发的是 application/x-www-form-urlencoded,服务端拿到这个 Content-Type 就会按表单去解析,你的 JSON 字符串会被整个当成一个字段名,然后报参数缺失。这个错我见过太多次了,报错信息还特别有迷惑性。
上面这条命令实际发出去的 HTTP 请求,内容大概是这样。
POST /api/posts HTTP/1.1
Host: localhost:3000
Content-Type: application/json; charset=UTF-8
{"title": "controller", "content": "what is controller"}
理解了这个映射关系,curl 就不再是一堆需要背的参数了,它就是让你手写一个 HTTP 报文而已。哪个参数对应报文的哪一部分,心里有数之后写命令就很快。
# 4.2 https 协议与双向认证
对接一些金融、政务类的接口时,服务端会要求客户端也提供证书,也就是双向 TLS。这时候要多传几个参数。
curl -v -k -X POST https://localhost:3000/api/posts \
--cert '/app/milo/tomcat/milogenius/webapps/client.crt' \
--key '/app/milo/tomcat/milogenius/webapps/client.key' \
--pass 'milogenius'
-k允许连接没有有效证书的 SSL 站点,跳过证书校验--cert客户端证书文件--key私钥文件--pass私钥密码
原文这几个参数写成了单横线的 -cert、-key、-pass,实际上它们都是长参数,必须写两个横线。还有一处原文把 URL 写成了 http://,既然是配 SSL 证书,协议头得是 https:// 才有意义,我一并改过来了。
-k 这个参数要谨慎。它确实能让「证书验证失败」这个报错立刻消失,但它同时也把 TLS 的身份校验整个关掉了,中间人可以随便冒充服务端。开发环境用自签证书时可以图方便加上,联调完记得去掉,绝不能带进生产脚本里。
# 五、HTTPS 证书报错怎么一层层定位
-k 是绕过问题,不是解决问题。真遇到证书报错,curl 是把问题缩小到具体哪一层最好用的工具。
先不加 -k 直接打一次,看 curl 给的是什么错。常见的几种表现不一样,对应的原因也完全不同。