📝 博客 · 2026-08-05 · ⏱ 25 分钟

cURL 命令怎么转成代码?常用参数速查与调试技巧

cURL 的 -X、-H、-d、-F、-u、-b、-k、-L、--data-urlencode 参数速查表,浏览器 Copy as cURL 的正确清理姿势,cURL 转 fetch / Python requests / OkHttp 的逐项映射,以及 -d 自动变 POST、Content-Type 默认值、引号转义、GET 带 body 等六个高频踩坑。

cURL接口调试后端开发

cURL 命令怎么转成代码?常用参数速查与调试技巧

同事甩来一条三行长的 cURL 命令让你「照着调一下」,或者你在 Chrome 里复制了一个请求想改写成 Python 脚本——这个转换过程看着机械,实际上 header 怎么搬、body 用哪种编码、cookie 放哪里,每一步都有坑。这篇文章给出 cURL 常用参数的完整速查表,讲清楚它到 fetch / requests / OkHttp 的映射关系,并列出六个几乎人人都栽过的细节。

cURL 常用参数速查

先把参数认全,后面的转换才有依据。

参数长写法作用备注
-X--request指定 HTTP 方法多数时候不需要写,见下文
-H--header添加请求头可重复多次
-d--data发送请求体会自动把方法变成 POST
--data-raw-d 但不解析 @ 前缀Chrome 复制出来的默认用这个
--data-binary原样发送,保留换行传文件内容、YAML 必须用它
--data-urlencode自动对值做 URL 编码值里有 &=、空格时用
-F--formmultipart/form-data 上传文件用 -F "file=@a.png"
-u--userHTTP Basic 认证格式 user:pass
-b--cookie发送 cookie-b "k=v; k2=v2"
-c--cookie-jar保存响应的 cookie 到文件-b 别搞混
-k--insecure跳过 TLS 证书校验仅调试用
-L--location自动跟随 301/302 重定向默认不跟随
-i--include输出里包含响应头调试必备
-v--verbose打印完整请求/响应过程排查握手问题
-s--silent隐藏进度条配合管道用
-o--output保存响应体到文件-O 用远端文件名
--compressed声明接受 gzip 并自动解压少了会看到乱码
-A--user-agent设置 UA等价于 -H "User-Agent: ..."
-e--referer设置 Referer绕防盗链常用
--connect-timeout连接超时(秒)配合 -m 总超时
-w--write-out输出自定义统计信息测耗时用

这张表里最值得记住的是 -d--data-raw--data-binary--data-urlencode 四兄弟的区别,它们决定了 body 到底以什么形态发出去,也是转代码时最容易出错的地方。

具体差在哪里:-d 会做两件「聪明过头」的事——把 @ 开头的值当成文件路径去读取,以及删掉内容里的所有换行和回车。--data-raw 关掉了第一条,所以值里含 @ 也不会被误解析,这也是浏览器复制出来默认用它的原因。--data-binary 两条都关掉,字节级原样发送。--data-urlencode 则多做一步 percent-encoding。一个简单的判断规则:传 JSON 用 --data-raw,传文件或多行文本用 --data-binary @file,传表单且值里有特殊字符用 --data-urlencode,其余场景 -d 够用。

另外 -H 有两个隐藏用法值得知道:写成 -H 'Header;'(冒号换成分号)可以发送一个空值的 header,写成 -H 'Header:'(冒号后什么都不写)则是删除 curl 默认会加的 header,比如 -H 'Expect:' 能去掉大 body 上传时那个恼人的 Expect: 100-continue

从浏览器复制 cURL:正确的清理姿势

Chrome / Edge / Firefox 的 DevTools 都支持在 Network 面板右键请求 → Copy → Copy as cURL。Windows 用户注意菜单里通常有两项:Copy as cURL (bash)Copy as cURL (cmd)转代码时一律选 bash 版,cmd 版的引号转义规则完全不同,几乎所有在线转换工具都按 bash 语法解析。

复制出来的命令大概长这样:

curl 'https://api.example.com/v1/orders?page=2' \
  -H 'accept: application/json' \
  -H 'accept-language: zh-CN,zh;q=0.9' \
  -H 'authorization: Bearer eyJhbGciOi...' \
  -H 'content-type: application/json' \
  -H 'sec-ch-ua: "Chromium";v="126", "Not)A;Brand";v="24"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-fetch-dest: empty' \
  -H 'cookie: sessionid=abc123; csrftoken=xyz789' \
  --data-raw '{"status":"paid","limit":20}' \
  --compressed

里面至少有一半 header 是噪音。 转代码前建议按这个优先级清理:

必须保留的authorizationcontent-typecookie(如果接口靠 session 鉴权)、以及业务方自定义的 header(如 x-tenant-idx-request-id)。

可以删掉的:所有 sec-ch-ua-*sec-fetch-* 开头的(浏览器安全元数据,服务端基本不看)、accept-languageaccept-encodingsec-ch-ua-platformreferer(除非有防盗链)、user-agent(除非服务端做了 UA 校验)。

需要判断的originreferer。如果服务端做了 CSRF 防护或者跨域白名单校验,这两个必须留;否则删掉。

清理后通常只剩三四行,可读性和可维护性都好得多。手动逐条改写容易漏 body 编码和 cookie 的对应关系,用cURL 转代码工具粘贴整条命令可以直接输出 fetch、requests、OkHttp、Axios 等多种写法,再按上面的原则删掉噪音 header 就是可用的代码。

cURL 到各语言的映射关系

核心就是把四样东西搬过去:方法、URL、header、body。难点在 body 和 cookie。

cURLfetch (JS)requests (Python)OkHttp (Java)
-X POSTmethod: 'POST'requests.post(...).post(body)
-H 'K: V'headers: { 'K': 'V' }headers={'K':'V'}.addHeader("K","V")
-d '{"a":1}' + JSON 头body: '{"a":1}'json={'a':1}RequestBody.create(json, JSON)
-d 'a=1&b=2'body: new URLSearchParams(...)data={'a':1,'b':2}FormBody.Builder()
-F 'f=@a.png'body: formDatafiles={'f': open(...)}MultipartBody.Builder()
-b 'k=v'headers: { Cookie: 'k=v' }cookies={'k':'v'}.addHeader("Cookie","k=v")
-u 'u:p'Authorization: Basic base64(u:p)auth=('u','p')Credentials.basic("u","p")
-L默认跟随(redirect:'follow'默认跟随.followRedirects(true)
-k浏览器做不到verify=False需自定义 TrustManager

拿上面那条命令转成三种写法:

// fetch
await fetch('https://api.example.com/v1/orders?page=2', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer eyJhbGciOi...',
    'Cookie': 'sessionid=abc123; csrftoken=xyz789'
  },
  body: JSON.stringify({ status: 'paid', limit: 20 })
});
# requests
import requests
r = requests.post(
    'https://api.example.com/v1/orders',
    params={'page': 2},
    headers={'Authorization': 'Bearer eyJhbGciOi...'},
    cookies={'sessionid': 'abc123', 'csrftoken': 'xyz789'},
    json={'status': 'paid', 'limit': 20},   # 自动加 Content-Type
    timeout=10
)
// OkHttp
MediaType JSON = MediaType.get("application/json; charset=utf-8");
Request req = new Request.Builder()
    .url("https://api.example.com/v1/orders?page=2")
    .addHeader("Authorization", "Bearer eyJhbGciOi...")
    .addHeader("Cookie", "sessionid=abc123; csrftoken=xyz789")
    .post(RequestBody.create("{\"status\":\"paid\",\"limit\":20}", JSON))
    .build();

有四个转换细节值得单独强调。

第一,json= 参数会自动设置 Content-Type: application/json,所以 requests 里不用再手写这个 header;但如果用 data= 传字符串,就必须自己加,否则会被当成表单。

第二,fetch 在浏览器里没法手动设 Cookie header。 Cookie 属于禁止修改的请求头,浏览器会忽略你的设置,只发同源自动携带的 cookie。想带 cookie 需要设 credentials: 'include' 并配合服务端 CORS 配置。只有 Node.js 环境(undici / node-fetch)里手动设 Cookie 才生效,这一点在转换工具生成的代码里经常被忽略。

第三,query 参数最好从 URL 里拆出来单独传。 requests 的 params=、OkHttp 的 HttpUrl.Builder().addQueryParameter() 都会自动处理编码,比手动往 URL 里拼字符串安全得多——尤其是参数值里有中文或 +&# 的时候。

第四,超时和重试是 cURL 命令里没有、但代码里必须补上的。 cURL 是一次性的人工操作,跑挂了你自己看得见;代码是跑在生产环境里的,不设超时意味着一个卡死的下游能把整个线程池拖垮。转换工具生成的代码通常不带超时,记得手动加上:requests 用 timeout=(3, 10) 分别设连接和读取超时,OkHttp 在 OkHttpClient.Builder() 上设 connectTimeout / readTimeout,fetch 则要配合 AbortSignal.timeout(10000)

六个高频踩坑

坑一:-d 会自动把请求方法变成 POST。

curl -d 'a=1' https://api.example.com/x        # 实际发的是 POST
curl -X GET -d 'a=1' https://api.example.com/x # 强制 GET,但 body 仍会发出去

所以只要用了 -d-X POST 就是多余的。反过来,看到 -X GET -d 这种组合要警惕:HTTP 规范不禁止 GET 带 body,但很多网关、CDN、反向代理会直接丢弃 GET 的 body,这个请求在本地能跑通、上线就失败的概率很高。正确做法是把参数放到 query string 里。

坑二:不写 Content-Type,-d 默认发的是 application/x-www-form-urlencoded

# 这条命令服务端收到的 Content-Type 是 form-urlencoded,
# 但 body 是 JSON 字符串 —— Spring 会返回 415,Express 会解析成空对象
curl -d '{"a":1}' https://api.example.com/x

# 正确写法
curl -H 'Content-Type: application/json' -d '{"a":1}' https://api.example.com/x

这是「curl 能跑代码不能跑」和「代码能跑 curl 不能跑」的头号原因。 排查接口 400/415 时,第一件事就是确认 Content-Type 和 body 格式是否匹配。

坑三:引号转义。 JSON body 里全是双引号,所以外层必须用单引号包裹。但 Windows 的 CMD 不认单引号,会把 '{"a":1}' 原样当成字符串的一部分。三种解法:

# 1. 用 Git Bash / WSL / PowerShell 7,直接用单引号
curl -d '{"a":1}' ...

# 2. CMD 下用双引号 + 反斜杠转义内部双引号
curl -d "{\"a\":1}" ...

# 3. 最稳的办法:body 写进文件,用 @ 引用
curl -d @body.json -H 'Content-Type: application/json' ...

第三种最值得推荐,body 稍微复杂一点就该放文件里,既避开转义地狱又方便版本管理。构造 JSON body 之前先用JSON 格式化工具校验一遍结构,比在终端里对着一长串转义字符找错括号高效得多。

坑四:-d 会吞掉换行符,--data-binary 不会。 如果你要 POST 一段 YAML、一个 SQL 语句或者 PEM 证书,用 -d 会把所有换行删掉,服务端解析必然失败。这种场景一律用 --data-binary @file

坑五:--data-urlencode-d 混用会出问题。 当参数值里含 &=+、空格时,-d 是原样发送的,服务端会把 & 当成参数分隔符切开:

# 错误:q 的值会被截断成 "a",b=c 变成了另一个参数
curl -d 'q=a&b=c' https://api.example.com/search

# 正确:值被编码成 a%26b%3Dc
curl --data-urlencode 'q=a&b=c' https://api.example.com/search

注意 --data-urlencode 只编码等号右边的值,不编码参数名。 涉及中文、时间戳带空格、Base64 字符串(里面可能有 +=)的参数尤其容易中招——Base64 的 + 在 URL 解码时会变成空格,这是签名校验失败的经典原因。不确定编码结果对不对,把值丢进URL 编码解码看一眼实际编出来的字符串。

坑六:-L 跟随重定向时方法会变。 按 HTTP 规范,301/302/303 重定向后,POST 会被转成 GET 且丢掉 body。如果你需要保持 POST,要显式加 --post301 --post302 --post303;307/308 则天然保持原方法和 body。转成代码时,requests 和 OkHttp 的行为和 curl 一致,写自动化脚本时要留意这个隐式转换。

用 cURL 调试线上接口的实战技巧

先用 -i 看响应头再看 body。 很多问题(跨域、缓存、限流、鉴权失败)的答案都在 header 里。-i 输出响应头,-v 连请求头和 TLS 握手一起打印。

-w 精确测耗时,定位是网络慢还是服务端慢:

curl -o /dev/null -s -w \
'DNS:%{time_namelookup}s 连接:%{time_connect}s TLS:%{time_appconnect}s 首字节:%{time_starttransfer}s 总计:%{time_total}s\n' \
https://api.example.com/health

如果 time_starttransfer 减去 time_appconnect 的差值很大,说明服务端处理慢;如果 time_namelookup 就很大,是 DNS 问题。

--resolve 绕过 DNS 直连某台机器,在灰度发布或排查单节点故障时极其有用:

curl --resolve api.example.com:443:10.0.1.23 https://api.example.com/health

这样域名、SNI、证书校验都正常,但实际连的是指定 IP,比改 hosts 干净得多。

-c-b 组合维持会话,模拟需要先登录的流程:

curl -c cookies.txt -d 'user=admin&pass=123' https://api.example.com/login
curl -b cookies.txt https://api.example.com/profile

遇到 TLS 报错先用 -v 看握手细节,别直接上 -k -k 只是把证书校验关了,问题依然在——常见原因是服务端证书链不完整(缺中间证书)、客户端 CA 根证书过期、或者 SNI 没匹配上。-v 输出里能看到 SSL certificate verify 的具体失败原因,对症下药比无脑跳过安全得多。-k 出现在任何生产代码里都是缺陷,转代码时如果原命令带了 -k,应该当成一个待办事项而不是照抄成 verify=False

响应是压缩的记得加 --compressed 现在服务端普遍开 gzip 或 br,如果你手动加了 -H 'accept-encoding: gzip' 却没加 --compressed,curl 不会自动解压,终端里会输出一堆二进制乱码。这也是从浏览器复制 cURL 后最常见的「明明浏览器里好好的,命令行就是乱码」的原因。

最后一条经验:把稳定的调试命令存成 shell 脚本或 .http 文件,变量用环境变量注入。团队里传 cURL 命令时顺手把 token 换成 $TOKEN,能避免不少凭据泄漏事故——聊天记录、工单系统、日志里躺着一个有效的 Bearer token,是安全审计中出现频率最高的问题之一。

总结

cURL 参数里真正影响转换结果的就四类:-X 定方法(用了 -d 就别再写)、-H 搬 header、-d 系列决定 body 形态、-b/-u 处理认证。转代码的本质是把这四样对应过去,其中 body 编码方式和 cookie 的落点是最容易错的两处。

从浏览器 Copy as cURL 时选 bash 版本,然后删掉所有 sec-ch-*sec-fetch-* 噪音 header,只留 authorization、content-type、cookie 和业务自定义头。

六个坑再复习一遍:-d 自动变 POST;不写 Content-Type 默认是 form-urlencoded;CMD 下引号要转义或干脆用 @file-d 吞换行要换 --data-binary;值里有 &=+ 空格必须用 --data-urlencode-L 跟 301/302 会把 POST 变 GET。

调试线上接口时,-i 看头、-w 测耗时、--resolve 直连节点、-c/-b 维持会话这四招基本能覆盖八成场景。转换代码这种机械活直接交给cURL 转代码工具,省下的时间留给真正需要判断的业务逻辑。