标签

cURL

cURL(Client URL)是一个广泛使用的命令行工具和库,用于传输数据,支持多种协议,包括 HTTP、HTTPS、FTP、SFTP 等。cURL 非常强大,它允许你在命令行中执行各种数据传输操作,如下载和上传文件,以及与服务器交互。

cURL
服务端5月29日 23:47
什么是 cURL?它在 Web 开发中有什么作用?cURL 是一个命令行数据传输工具,也是一套库,常用来通过 URL 发起 HTTP/HTTPS 请求。Web 开发里它最常见的作用是调试 API:不用写页面、不用打开 Postman,直接在终端复现 GET、POST、请求头、Cookie、鉴权和上传下载问题。 ## 追问 ### cURL 和 Postman 有什么区别? Postman 更适合可视化调试和团队管理接口;cURL 更轻、更容易复制到终端、脚本、CI/CD 或线上机器里排查问题。很多接口文档也会直接给 cURL 示例,因为它可执行、可复现。 ### cURL 在项目里通常怎么用? 前后端联调时,用它验证接口是否真的可访问;线上排障时,用它检查状态码、响应头、重定向和 TLS;自动化脚本里,用它做健康检查或触发 Webhook。 ### 它只支持 HTTP 吗? 不是。cURL 支持 HTTP、HTTPS、FTP、SFTP、SMTP 等多种协议,只是 Web 开发中最常用的是 HTTP/HTTPS。 ### 面试里怎么回答更像真用过? 可以说自己用 `curl -i` 看响应头,用 `-H` 带 token,用 `-d` 复现 POST 请求,用 `-v` 排查 DNS、TLS 或连接失败。这样比只背“命令行工具”更具体。 ## 写段代码 ```bash # 查看接口是否可访问 curl -i https://api.example.com/users # 带 Token 调 API curl -H "Authorization: Bearer token" \ https://api.example.com/profile # 提交 JSON curl https://api.example.com/users \ -H "Content-Type: application/json" \ -d '{"name":"John"}' ```
服务端5月29日 23:47
如何用 cURL 发送 GET 和 POST 请求?GET 用来从服务端取数据,参数通常放在 URL 查询串里;POST 用来提交数据,数据通常放在请求体里。用 cURL 时,GET 最常见的是直接请求 URL,POST 常用 `-d` 或 `--data` 传请求体。注意:`curl -d` 默认会发 POST,不一定非要写 `-X POST`。 ## 追问 ### GET 和 POST 最大区别是什么? GET 的参数暴露在 URL 中,适合查询;POST 的数据放在 body 中,适合提交表单、JSON 或文件。GET 通常可缓存、幂等;POST 通常不缓存,也不保证幂等。 ### `-d` 和 `-G` 一起用是什么意思? `-d` 默认把数据放进请求体并触发 POST;加 `-G` 后,cURL 会把这些参数拼到 URL 后面,仍然发 GET。 ### 实际调接口时最常用哪些参数? 常用 `-H` 加请求头,`-d` 传 JSON 或表单,`-i` 看响应头和响应体,`-I` 只看响应头,`-v` 排查连接、TLS、重定向等问题。 ### 发送 JSON 时容易踩什么坑? 最常见是忘了加 `Content-Type: application/json`,或者 JSON 字符串引号没转义好。另一个坑是误以为 `-X POST` 必须写,其实有 `-d` 时 cURL 已经会用 POST。 ## 写段代码 ```bash # GET:查询用户 curl "https://api.example.com/users?page=1&limit=10" # GET:带请求头 curl -H "Authorization: Bearer token" \ https://api.example.com/users # POST:提交 JSON curl https://api.example.com/users \ -H "Content-Type: application/json" \ -d '{"name":"张三","email":"zhangsan@example.com"}' # POST:上传文件 curl https://api.example.com/upload \ -F "file=@/path/to/file.pdf" ```
服务端5月29日 22:48
cURL 如何处理 HTTP 认证?Basic Auth/Bearer/OAuth 怎么用?三种认证:Basic Auth——curl -u user:pass URL(密码明文 base64 编码必须配合 HTTPS);Bearer Token——curl -H "Authorization: Bearer TOKEN" URL;OAuth 2.0——先 curl token 端点拿 access_token,再带 token 请求 API。cURL 无内置 OAuth 流程,需手动实现 token 获取和刷新。 ## 追问 ### -u 和 -H Authorization 有什么区别? -u user:pass 自动生成 Authorization: Basic 头,更简洁。-H 手动写原始头,适合精确控制。安全做法:用环境变量 -u user:$API_KEY 避免密码出现在 shell history。 ### Bearer Token 过期了怎么刷新? 脚本模式:先 curl refresh_token 端点拿新 access_token,替换环境变量,再请求 API。生产建议用 SDK 自动刷新。 ### OAuth 2.0 授权码流程怎么用 cURL 实现? 分三步:1.浏览器访问授权 URL 手动授权拿 code;2.curl -X POST token-url 换 token;3.curl -H "Authorization: Bearer TOKEN" 请求资源。第 1 步必须浏览器完成。 ### API Key 和 Bearer Token 有什么区别? API Key 是静态字符串,不过期除非手动轮换。Bearer Token 有过期时间,由 OAuth 签发,可精细控制权限范围(scope)。API Key 适合服务间调用,Bearer Token 适合用户级授权。 ### .netrc 文件有什么用? 存储登录凭证:machine api.example.com login user password pass。cURL 加 -n 自动读取,不用 -u 传密码。chmod 600 保护文件权限。
服务端5月28日 02:37
cURL 如何设置请求头(Headers)?在 cURL 中,请求头(Request Headers)用于向服务器传递元数据,比如认证凭证、内容类型、客户端标识等。API 调试和接口对接时,设置请求头是最常见的操作之一。 ## 基本语法 使用 `-H` 或 `--header` 参数添加请求头,格式必须为 `"Name: Value"`: ```bash curl -H "Header-Name: Header-Value" https://api.example.com ``` 设置多个请求头时,每个 `-H` 单独写一个: ```bash curl -H "Content-Type: application/json" \ -H "Authorization: Bearer token123" \ -H "Accept: application/json" \ https://api.example.com/users ``` ## 常用请求头 ```bash # Content-Type — 指定请求体格式 curl -H "Content-Type: application/json" \ -d '{"name":"test"}' \ https://api.example.com/users # Authorization — 身份认证(Bearer Token) curl -H "Authorization: Bearer your_token_here" \ https://api.example.com/protected # Authorization — HTTP Basic Auth curl -H "Authorization: Basic $(echo -n 'user:pass' | base64)" \ https://api.example.com/protected # Accept — 告诉服务器你期望的响应格式 curl -H "Accept: application/json" \ https://api.example.com/users # User-Agent — 标识客户端 curl -H "User-Agent: MyApp/1.0" \ https://api.example.com/data # Cookie — 发送 Cookie curl -H "Cookie: session_id=abc123; user_id=456" \ https://api.example.com/profile ``` | 请求头 | 用途 | 常见值 | |--------|------|--------| | Content-Type | 请求体格式 | application/json, application/x-www-form-urlencoded, multipart/form-data | | Authorization | 身份认证 | Bearer token, Basic base64(user:pass) | | Accept | 期望的响应格式 | application/json, text/html, */* | | User-Agent | 客户端标识 | Mozilla/5.0, MyApp/1.0 | | Cookie | 发送 Cookie | session_id=abc123 | | Cache-Control | 缓存控制 | no-cache, no-store | | Referer | 来源页面 | https://example.com/page | ## 快捷选项 cURL 为几个常用请求头提供了专用选项,比 `-H` 更简洁: ```bash # -A / --user-agent — 设置 User-Agent curl -A "MyApp/1.0" https://api.example.com/data # -e / --referer — 设置 Referer curl -e "https://example.com/page" https://api.example.com/data # -b / --cookie — 设置 Cookie(也支持从文件读取) curl -b "session_id=abc123; user_id=456" https://api.example.com/profile # -b 从文件读取 Cookie curl -b cookies.txt https://api.example.com/profile ``` ## 删除请求头 cURL 默认会发送一些内部请求头(如 Host、Accept、User-Agent)。如果想删除某个默认头,将值设为空: ```bash # 删除默认的 Accept 头 curl -H "Accept:" https://api.example.com # 删除默认的 User-Agent(某些反爬场景需要) curl -H "User-Agent:" https://api.example.com ``` 冒号后面没有任何内容,cURL 就不会发送该头部。 对于没有值的头部字段,在名称后加分号: ```bash curl -H "X-Empty-Header;" https://api.example.com ``` ## 从文件读取请求头 请求头较多时,可以写入文件,用 `@` 引用: ```bash # headers.txt 内容: # Content-Type: application/json # Authorization: Bearer token123 # X-Custom-Header: custom-value curl -H @headers.txt https://api.example.com/users ``` 每行一个请求头,格式与 `-H` 参数一致。 ## 验证请求头是否生效 加 `-v`(verbose)参数可以看到实际发出的请求头,以 `>` 开头的行就是发出的头部: ```bash curl -v -H "Authorization: Bearer token123" \ https://api.example.com/protected # 输出中可以看到: # > GET /protected HTTP/2 # > Host: api.example.com # > Authorization: Bearer token123 # > User-Agent: curl/8.1.2 # > Accept: */* ``` 也可以用 httpbin.org 快速验证,它会把收到的请求头原样返回: ```bash curl -H "X-Test: hello" https://httpbin.org/headers # 返回 JSON 中会显示你发送的所有请求头 ``` 如果只想看响应头(以 `<` 开头的行),用 `-I`(HEAD 请求)或 `-D -`(转储响应头到 stdout)。 ## 重复头部的处理 多次用 `-H` 设置同一个头部名称时,行为取决于 cURL 的内部实现: ```bash # 对标准头部(如 User-Agent),后者覆盖前者 curl -H "User-Agent: Agent1" -H "User-Agent: Agent2" URL # 实际发送:User-Agent: Agent2 # 对自定义头部,cURL 可能发送多个同名头部 curl -H "X-Custom: value1" -H "X-Custom: value2" URL # 可能发送两个 X-Custom 头部 ``` 大多数服务器按照 RFC 7230 将同名头部合并为逗号分隔的单个值。如果需要覆盖而非追加,用 `-v` 确认实际发送结果,或确保只写一次该头部。 ## 特殊场景 **发送压缩请求体:** ```bash # 告诉服务器请求体是 gzip 压缩的 curl -H "Content-Encoding: gzip" \ --data-binary @compressed.gz \ https://api.example.com/upload ``` **CORS 预检请求:** ```bash # 模拟浏览器发送 OPTIONS 预检 curl -X OPTIONS \ -H "Origin: https://example.com" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: X-Custom-Header" \ https://api.example.com/data ``` **带摘要认证(Digest Auth):** ```bash # curl 内置摘要认证支持,不必手动构造 Authorization 头 curl --digest -u user:pass https://api.example.com/protected ``` **发送 multipart 表单时自动设置的头部:** ```bash # -F 会自动设置 Content-Type: multipart/form-data; boundary=... # 不要手动设置 Content-Type,否则会覆盖 boundary curl -F "file=@photo.jpg" \ -F "name=test" \ https://api.example.com/upload ``` ## 常见踩坑 **1. 冒号后面没有空格** ```bash # 可能出问题 — 部分服务端解析失败 curl -H "Content-Type:application/json" URL # 推荐 — 冒号后加空格 curl -H "Content-Type: application/json" URL ``` HTTP 规范允许冒号后无空格,但实际中部分服务端解析会出问题,建议始终加空格。 **2. 值含特殊字符未加引号** ```bash # 错误 — 分号会被 shell 解释 curl -H Cookie: session=abc; user=123 URL # 正确 — 整个头用双引号包裹 curl -H "Cookie: session=abc; user=123" URL ``` **3. multipart 表单手动设置了 Content-Type** ```bash # 错误 — 覆盖了 boundary,服务端无法解析 curl -H "Content-Type: multipart/form-data" -F "file=@data.bin" URL # 正确 — 让 -F 自动设置 Content-Type curl -F "file=@data.bin" URL ``` **4. 后设置的头部覆盖前一个** ```bash # 最终 User-Agent 是 Agent2 curl -H "User-Agent: Agent1" \ -H "User-Agent: Agent2" \ URL ``` 对标准头部是覆盖行为,用 `-v` 确认实际发送结果。
服务端5月28日 02:37
cURL 如何处理 URL 编码和特殊字符?在 cURL 中处理 URL 编码和特殊字符是日常请求中绕不开的问题——查询参数里的空格、中文、`&` 和 `=` 都可能在传输中被误解析。理解 cURL 提供的编码机制,以及何时需要手动编码,能避免大量调试时间。 ### URL 编码的核心规则 URL 编码(Percent Encoding)将非安全字符转换为 `%XX` 格式,`XX` 是字符 UTF-8 字节的十六进制表示。RFC 3986 规定,只有字母、数字和 `-_.~` 属于无需编码的"未保留字符"。 ```bash # 常见字符的编码映射 空格 -> %20 & -> %26 = -> %3D + -> %2B % -> %25 # -> %23 中文 -> %E4%B8%AD%E6%96%87 ``` 需要区分两种编码场景:**URL 路径编码**遵循 RFC 3986,空格编码为 `%20`;**表单提交编码**(application/x-www-form-urlencoded)遵循 HTML 规范,空格编码为 `+`。cURL 的 `--data-urlencode` 使用的就是表单编码规则。 ### --data-urlencode 的四种语法 `--data-urlencode` 是 cURL 处理编码的主力参数,但它支持多种写法,行为各不相同: ```bash # 1. key=value:对 value 部分 URL 编码 curl --data-urlencode "name=hello world" https://api.example.com # 发送:name=hello+world(value 编码,key 不编码) # 2. =value:对整个 value 编码,不带 key curl --data-urlencode "=hello world" https://api.example.com # 发送:hello+world # 3. key@filename:读取文件内容作为 value 并编码 curl --data-urlencode "content@/tmp/payload.txt" https://api.example.com # 文件内容会被 URL 编码后作为 content 的值 # 4. @filename:读取文件内容并编码,不带 key curl --data-urlencode "@/tmp/raw_data.txt" https://api.example.com # 文件内容整体编码后发送 ``` 面试追问:为什么 `--data-urlencode "name=value"` 只编码 value 而不编码 key?因为 key 是开发者可控的固定字符串,通常不含特殊字符;而 value 来自用户输入,不可控,必须编码。 ### GET 请求中的编码:-G 配合 --data-urlencode `--data-urlencode` 默认以 POST 方式发送数据。加上 `-G`(或 `--get`)后,数据会被追加到 URL 查询字符串中: ```bash # 构建带编码的 GET 请求 curl -G https://api.example.com/search \ --data-urlencode "q=hello world" \ --data-urlencode "category=技术&编程" # 实际请求:https://api.example.com/search?q=hello+world&category=%E6%8A%80%E6%9C%AF%26%E7%BC%96%E7%A8%8B ``` 如果不加 `-G`,同样的命令会把数据放进请求体,变成 POST 请求——这是 cURL 新手最常犯的错误之一。 ### 手动编码:当 --data-urlencode 不够用时 有些场景下 `--data-urlencode` 无法覆盖需求,比如 URL 路径中包含中文、需要对整个 URL 做编码处理等。 ```bash # 使用 Python 编码(最通用) ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('hello world & 中文'))") curl "https://api.example.com/search?q=$ENCODED" # 使用 jq 编码(适合管道操作) ENCODED=$(jq -nr --arg s 'hello world' '$s | @uri') curl "https://api.example.com/search?q=$ENCODED" # 利用 cURL 自身做编码(巧妙但可读性差) encode() { local data="$(curl -s -o /dev/null -w '%{url_effective}' --get --data-urlencode "$1" "")" echo "${data#/?}" } ENCODED=$(encode "hello world & 中文") curl "https://api.example.com/search?q=$ENCODED" ``` ### 双编码陷阱 对已经编码过的字符串再次编码,会产生双重编码(double-encoding),这是最难排查的一类 bug: ```bash # 正确:只编码一次 curl -G https://api.example.com/search \ --data-urlencode "q=hello%20world" # 服务端收到:q=hello%20world → 解码为 "hello world" # 错误:--data-urlencode 又对 %20 做了一次编码 # 结果 %20 变成了 %2520 # 服务端收到:q=hello%2520world → 解码为 "hello%20world" ``` 避免方法:如果一个值已经是编码后的,不要再通过 `--data-urlencode` 处理。用 `-d` 代替,或者确保输入始终是未编码的原始值。 ```bash # 如果值已经是编码后的,用 -d 直接发送 curl -G https://api.example.com/search \ -d "q=hello%20world" # 如果值是原始值,用 --data-urlencode curl -G https://api.example.com/search \ --data-urlencode "q=hello world" ``` ### 查询参数中 & 和 = 的歧义 URL 中 `&` 是参数分隔符,`=` 是键值分隔符。当参数值本身包含这些字符时,不加编码会导致参数解析错误: ```bash # 错误:& 被误认为参数分隔符 curl "https://api.example.com/search?q=foo&bar" # 服务端理解为两个参数:q=foo 和 bar(无值) # 正确方式一:手动编码 curl "https://api.example.com/search?q=foo%26bar" # 正确方式二:用 --data-urlencode 自动处理 curl -G https://api.example.com/search \ --data-urlencode "q=foo&bar" ``` ### 路径中的特殊字符 URL 路径(`?` 之前的部分)中的特殊字符处理与查询参数不同。cURL 默认会对路径中的部分字符做处理: ```bash # 路径中的空格需要编码 curl "https://api.example.com/files/my%20document.pdf" # 路径中的中文需要编码 curl "https://api.example.com/files/%E4%B8%AD%E6%96%87%E6%96%87%E4%BB%B6.pdf" # --path-as-is:阻止 cURL 对路径做任何处理(保留原始路径) # 不加此参数时,cURL 会把 /../ 和 /./ 规范化 curl --path-as-is "https://api.example.com/../secret.txt" ``` ### JSON 请求体中的特殊字符 JSON 请求体不走 URL 编码,但有自己的转义规则——双引号、反斜杠、控制字符需要转义: ```bash # 直接写 JSON 时需要手动转义 curl -X POST https://api.example.com/users \ -H "Content-Type: application/json" \ -d '{"name":"张三","bio":"Line1\nLine2\tTabbed"}' # 用 jq 生成 JSON,自动处理转义(推荐) jq -n '{name: "张三", bio: "Line1\nLine2"}' | \ curl -X POST https://api.example.com/users \ -H "Content-Type: application/json" \ -d @- ``` ### 表单提交的混合编码 实际开发中经常需要同时发送已编码字段和待编码字段: ```bash curl -X POST https://api.example.com/submit \ -d "id=123&status=active" \ --data-urlencode "content=Special chars: & = ?" ``` `-d` 和 `--data-urlencode` 可以混用。cURL 会将所有数据合并为一条请求体,`-d` 的部分原样发送,`--data-urlencode` 的部分自动编码。 ### 调试编码问题的方法 编码问题的排查关键在于确认"实际发送的内容到底是什么": ```bash # 方法一:用 -v 查看完整请求(包含编码后的 URL) curl -v -G https://api.example.com/search \ --data-urlencode "q=hello world" # 方法二:用 --trace-ascii 把完整请求写入文件 curl --trace-ascii /tmp/trace.log -G https://api.example.com/search \ --data-urlencode "q=hello world" # 然后查看 trace.log 确认实际 URL # 方法三:用 -w 输出编码后的实际 URL curl -s -o /dev/null -w '%{url_effective}\n' -G https://api.example.com/search \ --data-urlencode "q=hello world" # 输出:https://api.example.com/search?q=hello+world ``` ### 实战脚本 ```bash #!/bin/bash # URL 编码处理封装 # 编码函数(依赖 python3) urlencode() { python3 -c "import urllib.parse, sys; print(urllib.parse.quote(sys.argv[1]))" "$1" } API_BASE="https://api.example.com/v1" # GET 请求:自动编码查询参数 curl -G "${API_BASE}/search" \ --data-urlencode "q=hello world & 中文" \ -H "Accept: application/json" # POST 请求:混合编码字段 curl -X POST "${API_BASE}/submit" \ -d "type=article" \ --data-urlencode "title=深入理解 cURL 编码" \ --data-urlencode "content=包含 & 和 = 的内容" # 路径中含中文:手动编码路径部分 ENCODED_NAME=$(urlencode "中文文件") curl "${API_BASE}/files/${ENCODED_NAME}.pdf" ``` 面试中常被追问的关键区别:`--data-urlencode` 编码的是 value 部分,`-d` 原样发送;表单编码空格变 `+`,路径编码空格变 `%20`;已编码的值不要再过 `--data-urlencode`,否则会双编码。掌握这三条,cURL 的编码问题基本不会踩坑。
服务端5月28日 02:37
cURL 性能优化有哪些关键手段?## 为什么要关注 cURL 性能 cURL 是后端开发、运维和测试中最常用的命令行 HTTP 工具。默认配置下,cURL 每次请求都重新建立 TCP 连接和 TLS 握手,批量调用时性能损耗显著。掌握超时、重试、连接复用、并发和压缩等优化手段,能让生产环境的 API 调用速度提升数倍。 ## 超时与速率控制 超时是生产环境的第一道防线。cURL 提供两级超时: - `--connect-timeout`:TCP 连接建立的最大等待时间 - `--max-time`:整个请求(含传输)的最大耗时 ```bash # 连接超时 10 秒,整体超时 30 秒 curl --connect-timeout 10 --max-time 30 https://api.example.com # 7.68.0+ 支持毫秒级超时 curl --connect-timeout 3.5 --max-time 10.5 https://api.example.com ``` 遇到慢速传输时,`--speed-time` 和 `--speed-limit` 可以主动中断卡住的连接: ```bash # 如果连续 5 秒速度低于 100 字节/秒,自动中断 curl --speed-time 5 --speed-limit 100 https://api.example.com/large-file.zip -O ``` 速率限制用 `--limit-rate`,在带宽敏感场景下控制下载速度: ```bash curl --limit-rate 1M https://example.com/large-file.zip -O ``` ## 重试机制 网络请求天生不可靠,重试是保障可靠性的核心手段。 ```bash # 失败自动重试 3 次 curl --retry 3 https://api.example.com # 重试间隔 2 秒,防止立即重试加重服务端压力 curl --retry 3 --retry-delay 2 https://api.example.com # 限定重试总耗时,避免无限等待 curl --retry 5 --retry-delay 1 --retry-max-time 30 https://api.example.com # 连接被拒绝时也重试(默认只重试超时类错误) curl --retry 3 --retry-connrefused https://api.example.com ``` 关键细节:`--retry` 默认只对超时、5xx 错误和连接失败重试,不会对 4xx 重试。如果需要针对特定 HTTP 状态码重试,需要脚本层面处理。`--retry-delay` 只在两次重试之间生效,首次请求不受影响。 ## 连接复用 HTTP Keep-Alive 是 cURL 性能优化中收益最高的一项。一次 TCP + TLS 握手通常需要 100-300ms,复用连接直接省掉这笔开销。 ```bash # 保持连接 60 秒 curl --keepalive-time 60 https://api.example.com ``` 命令行 cURL 在单次执行中自动复用连接。但跨进程调用时无法复用——这是命令行 cURL 的固有限制,需要 libcurl 句柄复用才能解决: ```c // libcurl 句柄复用示例 CURL *handle = curl_easy_init(); // 第一次请求 curl_easy_setopt(handle, CURLOPT_URL, "https://api.example.com/users"); curl_easy_perform(handle); // 第二次请求复用同一连接 curl_easy_setopt(handle, CURLOPT_URL, "https://api.example.com/products"); curl_easy_perform(handle); curl_easy_cleanup(handle); ``` 多 handle 场景下,用 `curl_share_setopt` 共享 DNS 缓存和 Cookie,进一步减少重复开销。 DNS 解析也有缓存收益。`--resolve` 可以跳过 DNS 查询: ```bash # 直接指定 IP,跳过 DNS 解析 curl --resolve api.example.com:443:203.0.113.50 https://api.example.com ``` 这对调试 CDN 回源、绕过 DNS 劫持、压测时固定后端 IP 都有用。 ## HTTP/2 多路复用 HTTP/2 在单条 TCP 连接上并行传输多个请求,从根本上解决了 HTTP/1.1 的队头阻塞问题: ```bash # 强制使用 HTTP/2 curl --http2 https://api.example.com # 优先协商 HTTP/2,失败回退 HTTP/1.1 curl --http2-prior-knowledge https://api.example.com ``` 配合连接复用,HTTP/2 的收益最大:多个 API 请求共享一条连接,省去多次握手和队头等待。适合微服务网关、GraphQL 批量查询等场景。 ## 并发请求 cURL 7.66+ 原生支持并行传输,使用 `-Z`(`--parallel`)标志: ```bash # 并行下载多个 URL curl -Z -OL https://example.com/a.json https://example.com/b.json # 控制最大并发数 curl -Z --parallel-max 5 -OL https://example.com/file{1..10}.zip ``` 旧版本用 shell 方式实现并发: ```bash # 后台进程 + wait for url in "${urls[@]}"; do curl -s "$url" -o "$(basename $url).json" & done wait # GNU Parallel,更精细的并发控制 cat urls.txt | parallel -j 4 curl -s {} -o {/}.json ``` 选择建议:少量 URL 用 `-Z` 即可,批量任务推荐 GNU Parallel,便于控制并发数和失败重试。 ## 压缩传输 `--compressed` 让 cURL 在请求头添加 `Accept-Encoding`,服务端返回压缩响应后自动解压: ```bash curl --compressed https://api.example.com ``` JSON API 响应通常能压缩 60-80%,对移动端和带宽敏感场景效果显著。也可以手动指定压缩算法: ```bash curl -H "Accept-Encoding: gzip, deflate, br" https://api.example.com ``` 注意:`--compressed` 在服务端不支持压缩时不会报错,cURL 会正常接收未压缩的响应。 ## TCP 与 TLS 优化 `--tcp-nodelay` 禁用 Nagle 算法,减少小包传输延迟,适合交互式 API 调用: ```bash curl --tcp-nodelay --tcp-fastopen https://api.example.com ``` TLS 握手是 HTTPS 请求中耗时的环节,优化点包括: ```bash # 强制使用 TLS 1.2 及以上(拒绝旧协议) curl --tlsv1.2 --tls-max tls1.3 https://api.example.com # TLS 会话复用(libcurl 句柄复用时自动生效) # CA 缓存减少证书链重复加载(libcurl 7.84+) ``` 生产环境务必验证证书,不要用 `-k` 跳过验证。密码等凭证不要写在命令行里: ```bash # -u 只输用户名,cURL 会提示输入密码 curl -u "username" https://api.example.com # 更好:用环境变量 curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com ``` ## 性能分析 `-w`(`--write-out`)是 cURL 性能诊断的核心工具,可以输出请求各阶段耗时: ```bash curl -w "DNS: %{time_namelookup}s Connect: %{time_connect}s SSL: %{time_appconnect}s TTFB: %{time_starttransfer}s Total: %{time_total}s Size: %{size_download}B Speed: %{speed_download}B/s " -o /dev/null -s https://api.example.com ``` 各指标含义: | 指标 | 含义 | 关注场景 | |------|------|----------| | `time_namelookup` | DNS 解析耗时 | CDN 选路、DNS 劫持排查 | | `time_connect` | TCP 连接建立耗时 | 网络延迟、连接池耗尽 | | `time_appconnect` | TLS 握手完成耗时 | 证书链过长、协议协商慢 | | `time_starttransfer` | 首字节到达耗时(TTFB) | 服务端处理慢、排队过长 | | `time_total` | 整体耗时 | 端到端性能评估 | 定位思路:如果 `time_namelookup` 高,查 DNS;`time_connect` 高,查网络或连接池;`time_appconnect` 高,查 TLS 配置;`time_starttransfer` 高但前面指标正常,查服务端。 把格式写入文件可以复用: ```bash cat > perf-format.txt << 'EOF' timestamp:%{time_total} dns:%{time_namelookup} connect:%{time_connect} ssl:%{time_appconnect} ttfb:%{time_starttransfer} size:%{size_download} speed:%{speed_download} EOF curl -w "@perf-format.txt" -o /dev/null -s https://api.example.com >> perf.log ``` ## 大文件与断点续传 ```bash # 分块下载 curl -r 0-10485760 https://example.com/large-file.zip -o part1.zip curl -r 10485761-20971520 https://example.com/large-file.zip -o part2.zip # 断点续传 curl -C - -O https://example.com/large-file.zip ``` 分块下载 + 断点续传组合使用:先分块下载,某块中断后用 `-C -` 续传,最后用 `cat part*.zip > large-file.zip` 合并。 流式处理避免大文件占满内存: ```bash # 流式处理 JSON 响应 curl -s https://api.example.com/stream | jq '.[] | .name' ``` ## 生产级脚本模板 ```bash #!/bin/bash # 生产级 API 调用脚本 API_URL="https://api.example.com/v1/data" TOKEN="${API_TOKEN:-$(cat ~/.api_token)}" TIMEOUT=30 RETRY=3 api_call() { curl -s -S --connect-timeout 10 --max-time "$TIMEOUT" --retry "$RETRY" --retry-delay 2 --retry-connrefused --compressed --tlsv1.2 -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Accept: application/json" -H "User-Agent: MyApp/1.0" -w " {"status":%{http_code},"time":%{time_total},"size":%{size_download}} " "$@" } response=$(api_call "$API_URL") if [ $? -eq 0 ]; then echo "$response" | jq '.' else echo "Request failed" >&2 exit 1 fi ``` 可维护性方面,常用配置写入 `~/.curlrc`: ``` verbose connect-timeout = 10 max-time = 60 retry = 3 ``` 日志监控脚本示例: ```bash #!/bin/bash while true; do STATUS=$(curl -w "%{http_code}" -o /dev/null -s --max-time 5 https://api.example.com/health) echo "$(date '+%Y-%m-%d %H:%M:%S') | Status: $STATUS" [ "$STATUS" != "200" ] && echo "API unhealthy!" | mail -s "API Alert" admin@example.com sleep 60 done ``` ## 优化效果对比 | 优化项 | 优化前 | 优化后 | 典型提升 | |--------|--------|--------|----------| | 连接复用 | 每次新建连接 | Keep-Alive | 延迟降低 30-50% | | 压缩传输 | 原始大小 | Gzip/Brotli | 体积减少 60-80% | | HTTP/2 多路复用 | 队头阻塞 | 单连接并行 | 并发延迟降低 50%+ | | 并发请求 | 串行执行 | 并行处理 | 吞吐提升 3-5 倍 | | DNS 缓存 | 每次解析 | --resolve/本地缓存 | 延迟降低 10-20ms | | 断点续传 | 重新下载 | 续传 | 节省已完成部分的带宽 | ## 追问 - cURL 的 `--retry` 和应用层面的指数退避重试有什么区别?什么时候该用哪种? - 命令行 cURL 的连接复用有什么局限?libcurl 句柄复用如何突破这个限制? - `--compressed` 在服务端不支持压缩时行为是什么?会不会导致请求失败? - HTTP/2 多路复用和 `-Z` 并行传输有什么区别?各自适合什么场景?
服务端5月28日 02:36
如何使用 cURL 进行 API 调试和排错?cURL 是开发者在 API 开发中最常接触的命令行工具,但多数人只停留在 `curl -X GET` 的层面。遇到请求超时、证书报错、重定向异常等问题时,如果不知道 cURL 的调试参数,排查就像盲人摸象。 ## -v:你的第一道诊断线 `-v`(verbose)是 cURL 调试的核心开关,它会输出完整的请求-响应交互过程: ```bash curl -v https://api.example.com/users ``` 输出中以不同前缀区分信息来源: - `>` 发出的请求行和请求头 - `<` 收到的响应头 - `*` 连接建立和 TLS 握手细节 当 API 返回 401 时,先看 `>` 部分确认 Authorization 头是否真的发出去了;返回 301/302 时,看 `< Location` 确认跳转目标。大部分问题在 `-v` 输出中就能定位。 ## -w:量化请求的每个阶段 `-w`(write-out)把请求拆解为可量化的时间指标,是定位性能瓶颈的关键: ```bash curl -w "DNS: %{time_namelookup}s\nTCP: %{time_connect}s\nTLS: %{time_appconnect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \ -o /dev/null -s https://api.example.com ``` 各指标的含义和典型排查思路: | 指标 | 含义 | 偏高时排查方向 | |------|------|----------------| | `time_namelookup` | DNS 解析耗时 | 检查 DNS 配置或切换 DNS 服务器 | | `time_connect` | TCP 连接耗时 | 网络链路问题或服务端负载高 | | `time_appconnect` | TLS 握手耗时 | 证书链过长或协商算法不匹配 | | `time_starttransfer` | 首字节时间(TTFB) | 服务端处理慢,需排查后端逻辑 | | `time_total` | 请求总耗时 | 综合判断,大文件传输时主要受下载速度影响 | 如果 `time_namelookup` 占了总时间的 80%,问题在 DNS 而非服务端;如果 TTFB 正常但 `time_total` 很高,说明是响应体太大或网络带宽瓶颈。 ## --trace:完整的请求审计日志 `-v` 不够用时,`--trace-ascii` 记录每一个字节的收发: ```bash # 文本格式日志(可读性好) curl --trace-ascii debug.log https://api.example.com # 十六进制格式(排查编码/二进制问题) curl --trace debug.hex https://api.example.com ``` `--trace` 会记录请求体和响应体的完整内容,适合排查 POST 请求的 body 是否正确发送、响应中是否存在隐藏字符等问题。注意敏感信息(如 token)也会被记录,不要在生产环境随意使用。 ## 超时与重试:让请求可控 线上环境的请求不能无限等待,必须设置超时: ```bash # 连接超时 5 秒,整体超时 10 秒 curl --connect-timeout 5 --max-time 10 https://api.example.com # 失败后重试 3 次,间隔 2 秒 curl --retry 3 --retry-delay 2 https://api.example.com ``` `--connect-timeout` 控制 TCP 建连阶段的最大等待时间,`--max-time` 控制整个请求(含下载)的上限。两者要配合使用——只设 `--connect-timeout` 而不设 `--max-time`,慢速下载仍可能卡住。 ## SSL/TLS 证书问题排查 证书相关错误是 HTTPS 请求中最常见的坑: ```bash # 查看证书链和协商细节 curl -v https://api.example.com 2>&1 | grep -A 10 "SSL connection" # 跳过证书验证(仅限本地调试) curl -k https://self-signed.badssl.com # 指定 CA 证书 curl --cacert /path/to/ca.crt https://api.example.com # 指定客户端证书(mTLS 场景) curl --cert client.pem --key client-key.pem https://mtls.example.com ``` `-k` 只能用于临时测试,永远不要在正式环境跳过证书验证。生产环境中遇到证书错误,应使用 `--cacert` 指定正确的 CA 证书或 `--resolve` 绕过 DNS 指向正确的服务端。 ## 重定向与认证的隐藏陷阱 cURL 默认不跟随重定向,需要加 `-L`: ```bash # 跟随重定向并显示每一步的状态码 curl -L -v https://example.com 2>&1 | grep -E "(< HTTP|< Location)" ``` 重定向有一个容易被忽略的安全行为:当 Location 指向不同域名时,cURL 会自动丢弃 `Authorization` 头,防止凭据泄露到第三方。如果需要跨域传递认证信息,需要显式用 `-H` 重新添加。 ## DNS 与网络层诊断 当请求连不上服务器时,从 DNS 和网络层开始排查: ```bash # 指定 DNS 服务器(绕过本地 DNS 污染) curl --dns-servers 8.8.8.8 https://api.example.com # 手动映射域名到 IP(跳过 DNS 解析) curl --resolve example.com:443:192.168.1.100 https://example.com # 强制 IPv4(IPv6 连接异常时排查) curl -4 https://api.example.com # 测试端口连通性 curl -v telnet://example.com:80 ``` `--resolve` 在调试负载均衡、灰度发布时特别有用——可以把域名直接指向特定后端实例,绕过 LB 层。 ## 组合调试实战脚本 把常用的调试步骤封装成脚本,遇到问题直接执行: ```bash #!/bin/bash # api-debug.sh — 快速诊断 API 请求 URL=$1 [ -z "$URL" ] && echo "Usage: $0 <url>" && exit 1 echo "=== 连通性检查 ===" curl -o /dev/null -s -w "HTTP %{http_code} | TTFB %{time_starttransfer}s | Total %{time_total}s\n" "$URL" echo "" echo "=== 响应头 ===" curl -s -I "$URL" echo "" echo "=== 重定向链 ===" curl -s -I -L "$URL" 2>&1 | grep -E "^(HTTP|Location)" echo "" echo "=== 各阶段耗时 ===" curl -o /dev/null -s -w "DNS %{time_namelookup}s | TCP %{time_connect}s | TLS %{time_appconnect}s | TTFB %{time_starttransfer}s | Total %{time_total}s\n" "$URL" ``` 用法:`bash api-debug.sh https://api.example.com/users` ## 面试高频追问 **Q: -v 和 --trace-ascii 有什么区别?** `-v` 只输出请求头和响应头,不记录 body;`--trace-ascii` 记录完整的请求和响应内容,包括 body。排查请求体问题时必须用 `--trace-ascii`。 **Q: curl -w 的 time_starttransfer 和 time_total 差异大说明什么?** TTFB 小但 Total 大,说明服务端响应快但传输慢——可能是响应体太大、网络带宽不足,或服务端在流式传输。反之如果 TTFB 本身就高,瓶颈在服务端处理速度。 **Q: 为什么 curl -L 跟随重定向后 Authorization 头丢了?** 这是 cURL 的安全设计。当重定向目标与原始域名不同时,cURL 自动移除敏感头(Authorization、Cookie 等),防止凭据被发送到第三方服务器。这是 RFC 7235 的安全要求。
服务端5月28日 02:35
cURL 如何实现文件上传功能?cURL 支持多种文件上传方式,核心区别在于 `Content-Type` 和请求体的组织形式。面试中最高频考察的是 `-F` 表单上传和 `-T` PUT 上传的区别。 ### -F 表单上传(multipart/form-data) `-F` 是最常用的上传方式,模拟浏览器表单提交,自动设置 `Content-Type: multipart/form-data`: ```bash # 基本上传 curl -X POST https://api.example.com/upload \ -F "file=@/path/to/document.pdf" # 指定 MIME 类型(服务器可能根据类型做不同处理) curl -X POST https://api.example.com/upload \ -F "file=@/path/to/image.png;type=image/png" # 指定服务器端接收的文件名 curl -X POST https://api.example.com/upload \ -F "file=@/path/to/local.txt;filename=uploaded.txt" ``` `@` 符号告诉 cURL 读取文件内容而非当作普通字符串。如果误写成 `-F "file=/path/to/file"`,服务器收到的是字面字符串而非文件内容,这是新手最常见的错误。 ### -T PUT 上传(原始文件流) `-T` 或 `--upload-file` 将文件作为请求体直接发送,默认使用 HTTP PUT 方法: ```bash # HTTP PUT 上传 curl -T /path/to/file.pdf https://api.example.com/files/document.pdf # FTP 上传 curl -T /path/to/file.zip ftp://ftp.example.com/upload/ \ --user username:password # SFTP 上传 curl -T /path/to/file.zip sftp://example.com/upload/ \ --user username:password ``` `-T` 与 `-F` 的关键区别:`-T` 发送的是原始文件流,`Content-Type` 默认为 `application/octet-stream`,不会封装成 multipart 格式。RESTful API 中资源更新(如替换已有文件)常用这种方式。 ### 多文件上传 ```bash # 不同字段名上传多个文件 curl -X POST https://api.example.com/upload \ -F "avatar=@/path/to/avatar.jpg" \ -F "resume=@/path/to/resume.pdf" # 数组形式上传(后端用 files[] 接收) curl -X POST https://api.example.com/upload \ -F "files[]=@/path/to/file1.pdf" \ -F "files[]=@/path/to/file2.jpg" # 混合文件和普通表单字段 curl -X POST https://api.example.com/submit \ -F "name=张三" \ -F "email=zhangsan@example.com" \ -F "avatar=@/path/to/avatar.jpg" ``` ### 二进制上传与 Base64 编码 直接发送原始二进制数据,适用于 API 要求 `application/octet-stream` 的场景: ```bash curl -X POST https://api.example.com/upload \ -H "Content-Type: application/octet-stream" \ --data-binary @/path/to/file.bin # 从标准输入读取 cat file.bin | curl -X POST https://api.example.com/upload \ -H "Content-Type: application/octet-stream" \ --data-binary @- ``` 某些 API 只接受 JSON 请求体,此时需要 Base64 编码: ```bash curl -X POST https://api.example.com/upload \ -H "Content-Type: application/json" \ -d "{\"file\":\"$(base64 -w 0 /path/to/file.pdf)\",\"filename\":\"document.pdf\"}" ``` Base64 编码会使数据体积增加约 33%,大文件场景下应优先使用 `-F` 表单上传。 ### 大文件上传与断点续传 ```bash # 显示上传进度条 curl -X POST https://api.example.com/upload \ -F "file=@/path/to/large.zip" \ --progress-bar # 断点续传(需要服务器支持 Range 或 resumable upload) curl -C - -X POST https://api.example.com/upload \ -F "file=@/path/to/large.zip" # 设置超时避免长时间挂起 curl --max-time 600 -F "file=@large.zip" \ https://api.example.com/upload ``` `-C -` 表示自动从上次中断的位置继续传输。注意:真正的断点续传需要服务端支持,对于 multipart 上传,多数服务器并不支持续传,这时需要使用服务端提供的分块上传 API(先获取 uploadId,逐块上传后合并)。 ### 带认证的上传 ```bash # Bearer Token 认证 curl -X POST https://api.example.com/upload \ -H "Authorization: Bearer your_token" \ -F "file=@/path/to/file.pdf" # AWS S3 预签名 URL 上传(PUT 方式) curl -X PUT "https://presigned-url-here" \ -H "Content-Type: application/pdf" \ --data-binary @/path/to/file.pdf # 基本认证 curl -u username:password -F "file=@file.pdf" \ https://api.example.com/upload ``` ### 关键参数速查 | 参数 | 作用 | 适用场景 | |------|------|----------| | `-F` / `--form` | multipart 表单上传 | Web 表单、API 文件字段 | | `-T` / `--upload-file` | PUT 原始文件流上传 | RESTful 资源替换、FTP/SFTP | | `@` | 读取文件内容 | `-F` 和 `--data-binary` 中 | | `;type=` | 指定 MIME 类型 | `-F` 中覆盖自动检测 | | `;filename=` | 指定服务端文件名 | `-F` 中需要改名的场景 | | `--data-binary` | 发送原始二进制数据 | `application/octet-stream` | | `-C -` | 断点续传 | 大文件中断后恢复 | | `--progress-bar` | 显示进度条 | 大文件上传监控 | ### 面试常见追问 **-F 和 -T 上传有什么区别?** `-F` 封装为 multipart/form-data 格式,可以同时传文件和其他字段,适合表单场景。`-T` 发送原始文件流作为请求体,默认 PUT 方法,适合直接替换资源或 FTP/SFTP 上传。 **上传文件时 @ 符号的作用是什么?** `@` 告诉 cURL 读取后面路径的文件内容。不加 `@` 时,cURL 会把路径字符串当作普通值发送。 **如何上传超过服务器限制的大文件?** 分两种情况:如果是服务器 `Content-Length` 限制,需服务端调整配置;如果是需要分块上传,要调用服务端提供的 chunked upload API,cURL 本身不自动分块。
服务端5月28日 01:54
cURL 如何处理 HTTP 重定向?cURL 处理 HTTP 重定向是日常开发和面试中的高频考点。理解 cURL 重定向机制,核心在于掌握三个层面:跟随重定向的开关控制、不同状态码下的方法切换行为、以及跨域重定向的安全边界。 ## 面试核心答案 cURL 默认**不跟随重定向**,需加 `-L`(或 `--location`)才会自动追踪 3xx 响应。重定向时,301/302/303 会将 POST 请求降级为 GET,而 307/308 会保持原始方法。通过 `--post301`/`--post302` 可以覆盖默认行为。跨域重定向时,cURL 默认不转发 Authorization 等敏感头,需 `--location-trusted` 显式允许。 ## 重定向的基本控制 cURL 遇到 3xx 响应时,默认只返回重定向响应头,不会自动跳转到新地址。使用 `-L` 开启跟随重定向: ```bash # 默认:不跟随,只返回 301/302 响应 curl http://example.com # 跟随重定向到最终地址 curl -L http://example.com curl --location http://example.com ``` 用 `-v` 可以看到完整的重定向过程,包括每一跳的 Location 头和状态码,排查问题时非常实用: ```bash curl -L -v http://example.com ``` ## 为什么 301/302 会把 POST 变成 GET 这是面试中最常追问的点。原因在于 HTTP 规范的历史演进: - **301 Moved Permanently** 和 **302 Found** 在 RFC 1945(HTTP/1.0)时代就定义了,当时的客户端实现普遍把重定向后的 POST 改为 GET。后来 RFC 7231 将这个行为正式标准化——301/302 重定向后,客户端"可以"将方法改为 GET。 - **303 See Other** 是 HTTP/1.1 新增的,明确要求重定向后必须使用 GET,典型场景是 POST 表单提交后跳转到结果页。 - **307 Temporary Redirect** 和 **308 Permanent Redirect** 是后来补充的状态码,设计动机就是解决 301/302 方法降级的问题。307/308 明确要求保持原始请求方法不变。 | 状态码 | 含义 | 方法变化 | 适用场景 | | --- | --- | --- | --- | | 301 | 永久重定向 | POST → GET | 网址永久迁移 | | 302 | 临时重定向 | POST → GET | 临时跳转(旧规范) | | 303 | 另见其他 | POST → GET | POST 后跳结果页 | | 307 | 临时重定向 | 保持不变 | API 临时切换地址 | | 308 | 永久重定向 | 保持不变 | API 永久迁移 | 在 cURL 中控制 POST 重定向行为: ```bash # 默认:301/302 重定向后 POST 变 GET curl -L -X POST -d "data=test" http://example.com/submit # 301 重定向时保持 POST(遵循 RFC 7231) curl -L --post301 -X POST -d "data=test" http://example.com/submit # 302 重定向时保持 POST curl -L --post302 -X POST -d "data=test" http://example.com/submit # 303 重定向时保持 POST curl -L --post303 -X POST -d "data=test" http://example.com/submit ``` ## 重定向次数限制 cURL 默认最多跟随 50 次重定向,超过则报错。生产环境中应主动设置合理上限,防止重定向循环: ```bash # 限制最多 5 次重定向 curl -L --max-redirs 5 http://example.com # 允许无限重定向(有循环风险,不建议) curl -L --max-redirs -1 http://example.com ``` ## 查看重定向链 调试重定向问题时,需要知道中间经历了哪些跳转。cURL 提供了 `-w` 格式化输出来获取关键信息: ```bash # 查看最终到达的 URL curl -L -w "Final URL: %{url_effective}\n" -o /dev/null -s http://example.com # 查看总重定向次数 curl -L -w "Redirects: %{num_redirects}\n" -o /dev/null -s http://example.com # 同时获取最终 URL、重定向次数和状态码 curl -L -w "\n最终URL: %{url_effective}\n重定向次数: %{num_redirects}\nHTTP状态: %{http_code}\n" \ -o /dev/null -s http://example.com ``` 用 `-v` 配合 grep 可以查看每一跳的 Location 头: ```bash curl -L -v http://example.com 2>&1 | grep -E "(< HTTP|< Location)" ``` ## 跨域重定向与安全 cURL 在跨域重定向时的安全行为容易被忽视,也是面试加分项: **敏感头不自动转发。** 当重定向到不同域名时,cURL 默认不会转发 `Authorization`、`Cookie` 等敏感请求头。这是安全设计——防止凭据意外泄露给第三方域名。 ```bash # 跨域重定向时,Authorization 不会发给新域名 curl -L -H "Authorization: Bearer token123" http://api.example.com/old # 显式允许转发敏感头(仅在信任目标域名时使用) curl -L --location-trusted -H "Authorization: Bearer token123" \ http://api.example.com/old ``` **Cookie 的 SameSite 限制。** 现代浏览器的 SameSite 策略会影响跨站重定向时的 Cookie 携带,但 cURL 作为命令行工具不受此限制。cURL 中 Cookie 的跨域行为完全由 `-c`(写入 cookie jar)和 `-b`(发送 cookie)参数控制: ```bash # 手动管理跨域重定向的 Cookie curl -L -c cookies.txt -b cookies.txt -v http://example.com ``` **限制重定向协议。** 安全场景下,可以阻止 HTTP→HTTPS 之外的重定向,防止降级攻击: ```bash # 只允许重定向到 HTTPS curl -L --proto-redir =https http://example.com ``` ## 常见问题与排查 **重定向循环。** 服务端配置错误可能导致 A→B→A 的无限循环。用 `--max-redirs` 设置上限,观察 `-v` 输出中的循环跳转即可定位。 **POST 数据丢失。** 301/302 重定向后 POST 变 GET,请求体丢失。解决方案:使用 `--post301`/`--post302`,或让服务端返回 307/308。 **HTTPS 证书错误导致重定向失败。** 从 HTTP 重定向到 HTTPS 时,如果目标证书有问题会中断。开发环境可用 `-k` 跳过验证,生产环境必须修复证书。 ```bash # 开发环境临时跳过 SSL 验证 curl -L -k https://example.com ``` ## 实战:完整的重定向感知请求 将关键参数组合使用,构建一个对重定向行为完全可控的请求: ```bash curl -L --max-redirs 5 \ --post302 \ --proto-redir =https \ -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer token123" \ -d '{"action":"submit"}' \ -w "\n最终URL: %{url_effective}\n重定向次数: %{num_redirects}\nHTTP状态: %{http_code}\n" \ http://api.example.com/submit ``` 这段命令同时控制了:跟随重定向(`-L`)、次数上限(`--max-redirs 5`)、POST 方法保持(`--post302`)、只允许重定向到 HTTPS(`--proto-redir =https`)、以及最终结果的格式化输出。 ## 追问方向 - **cURL 能处理 HTML meta 重定向或 JavaScript 重定向吗?** 不能。cURL 只处理 HTTP 层的 3xx 响应,不解析 HTML 也不执行 JS。 - **`--location-trusted` 有什么安全风险?** 会将 Authorization 等敏感头转发给重定向目标域名,如果目标不可信(如开放重定向漏洞),凭据会被窃取。 - **重定向链中如何逐跳传递自定义头?** cURL 默认只对首次请求添加自定义头,重定向后的请求不保留。需要用 `-H` 配合 `--location-trusted` 或通过重定向后 URL 的路径判断手动处理。
服务端5月28日 01:54
cURL 的 -v、-i、-I、-s 参数有什么区别?cURL 是后端开发和运维中最常用的命令行工具之一,而 `-v`、`-i`、`-I`、`-s` 这四个参数控制着输出的内容和格式。很多人混用它们,实际上它们的输出目标、请求方式和适用场景完全不同。 ### 一张图看清区别 | 参数 | 全称 | 请求方法 | 输出内容 | 输出目标 | | ---- | -------------- | ----- | ------------- | ------ | | `-v` | `--verbose` | 不改变 | 连接过程 + 请求头 + 响应头 | stderr | | `-i` | `--include` | 不改变 | 响应头 + 响应体 | stdout | | `-I` | `--head` | HEAD | 仅响应头 | stdout | | `-s` | `--silent` | 不改变 | 仅响应体(无进度条) | stdout | > 关键区别:`-v` 的输出写入 stderr,其余三个写入 stdout。这意味着 `-v` 不会干扰管道操作,而 `-i` 和 `-I` 的响应头会混入 stdout 数据流。 ### 逐个拆解 #### -v:看懂完整的通信过程 ```bash curl -v https://api.example.com ``` 输出长这样: ``` * Trying 93.184.216.34:443... * Connected to api.example.com (93.184.216.34) port 443 * SSL connection using TLS_AES_256_GCM_SHA384 > GET / HTTP/1.1 > Host: api.example.com > User-Agent: curl/8.1.2 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: application/json < Content-Length: 42 < {"status":"ok"} ``` 输出符号的含义: - `*` 开头 —— 连接和 TLS 握手信息 - `>` 开头 —— 发送给服务器的请求头 - `<` 开头 —— 服务器返回的响应头 **为什么 -v 写入 stderr 而不是 stdout?** 这是 curl 的设计哲学:stdout 留给实际数据(响应体),stderr 留给诊断信息。所以你可以这样用: ```bash # 调试信息存文件,响应体正常输出 curl -v https://api.example.com 2>debug.log # 管道不会被打断 curl -v https://api.example.com 2>/dev/null | jq .status ``` #### -i:响应头和响应体一起拿 ```bash curl -i https://api.example.com ``` 输出: ``` HTTP/1.1 200 OK Content-Type: application/json Content-Length: 42 Date: Mon, 01 Mar 2026 10:00:00 GMT {"status":"ok"} ``` 响应头和响应体之间用一个空行分隔。-i 不改变请求方法,该发 GET 还是 GET,该发 POST 还是 POST,只是把响应头也一并输出。 **注意**:因为响应头混入了 stdout,直接管道给 `jq` 会解析失败。解决办法: ```bash # 用 -D - 把响应头写到 stderr,响应体单独给 jq curl -s -D - https://api.example.com 2>/dev/null | jq . ``` #### -I:只看响应头,不发请求体 ```bash curl -I https://api.example.com ``` 输出: ``` HTTP/1.1 200 OK Content-Type: application/json Content-Length: 42 Date: Mon, 01 Mar 2026 10:00:00 GMT ETag: "abc123" Cache-Control: max-age=3600 ``` `-I` 会把请求方法改成 HEAD。大多数服务器对 HEAD 请求和 GET 请求返回相同的响应头,但不是所有: - 有些 CDN 对 HEAD 请求返回的 `Content-Length` 可能为 0 - 某些 API 网关对 HEAD 和 GET 的路由规则不同 - 少数服务器直接拒绝 HEAD 请求 如果需要 GET 请求的响应头,用这个代替: ```bash curl -s -D - -o /dev/null https://api.example.com ``` #### -s:安静干活,只出结果 ```bash # 不显示进度条,只输出响应体 curl -s https://api.example.com ``` `-s` 关闭进度条和错误信息,但不会关闭响应体输出。在脚本里特别有用: ```bash # 获取数据并解析 response=$(curl -s https://api.example.com/users) echo "$response" | jq ".[] | .name" ``` **-s 的坑**:静默模式下连错误信息也被吞了。网络超时、DNS 解析失败都不会有任何提示。所以实际使用中,`-s` 几乎总是和 `-S` 搭配: ```bash # 静默但保留错误输出 curl -sS https://api.example.com ``` ### 常见组合技巧 ```bash # 检查 HTTP 状态码(脚本健康检查) curl -s -o /dev/null -w "%{http_code}" https://api.example.com # 调试 SSL 证书问题 curl -v https://api.example.com 2>&1 | grep -E "SSL|TLS|certificate" # 静默拿响应头(实际发 GET 请求) curl -s -D - -o /dev/null https://api.example.com # 完整调试 + 保存日志 curl -v https://api.example.com -o response.json 2>debug.log # 检查 CDN 缓存是否命中 curl -I -s https://cdn.example.com/image.jpg | grep -i "x-cache" ``` ### 面试追问方向 **Q: -v 和 -i 都能看到响应头,有什么本质区别?** A: 三个关键区别:(1)-v 同时显示请求头和响应头,-i 只显示响应头;(2)-v 还包含连接过程(DNS、TLS 握手),-i 只包含最终响应;(3)-v 写入 stderr,-i 写入 stdout,管道处理时行为完全不同。 **Q: 为什么 curl -I 有时候拿不到正确的 Content-Length?** A: 因为 -I 发送的是 HEAD 请求,有些服务器对 HEAD 请求返回的 Content-Length 为 0 或者不返回该字段。如果需要 GET 请求的真实 Content-Length,应该用 `curl -s -D - -o /dev/null` 代替。 **Q: -sS 组合是什么意思?为什么不直接用 -s?** A: -s 关闭了进度条和所有错误输出,包括网络超时、连接拒绝等重要信息。-S(--show-error)在 -s 模式下重新启用错误输出。脚本中几乎总是需要 -sS 而非单独 -s,否则故障排查时完全不知道请求为什么失败。 ### 速查表 | 你想做什么 | 用什么参数 | | ---------------- | ------------------ | | 排查请求为什么没发出去 | `-v` | | 看服务器返回了什么响应头 | `-i` 或 `-D -` | | 只检查状态码或缓存头 | `-I` 或 `-I -s` | | 脚本里安静拿数据 | `-sS` | | 获取 HTTP 状态码 | `-s -o /dev/null -w "%{http_code}"` | | 保存调试日志 | `-v 2>debug.log` |
服务端5月28日 01:53
cURL 和 wget 有什么区别?cURL 和 wget 是 Linux 系统中最常用的两个命令行网络工具,面试中经常被放在一起考察。很多候选人只能说出"cURL 能测 API,wget 能递归下载",但这远远不够。这道题的真正考点在于理解两个工具的**设计哲学差异**以及**底层架构不同**如何决定了它们各自的适用场景。 ### 一句话概括核心区别 **cURL 是网络传输的瑞士军刀,wget 是文件下载的可靠管家。** cURL 的设计目标是做一个通用的数据传输工具——它不关心你传什么、怎么传,只管把数据从 A 搬到 B。wget 的设计目标是可靠地下载文件——网络断了自动重试,链接坏了自动转换,一切都为了让文件安稳落地。 这个底层设计哲学的差异,直接导致了两者在协议支持、功能特性和使用场景上的全面分歧。 ### 核心差异对比 | 维度 | cURL | wget | |------|------|------| | 设计目标 | 通用数据传输 | 可靠文件下载 | | 底层架构 | 基于 libcurl 库 | 独立程序,无库形式 | | 协议支持 | 20+ 种(HTTP/1.1、HTTP/2、HTTP/3、FTP、SFTP、SCP、SMTP、LDAP 等) | HTTP/HTTPS/FTP | | 数据方向 | 双向(上传 + 下载) | 单向(仅下载) | | 递归下载 | 不支持 | 支持,可镜像整站 | | 并行传输 | 支持(7.66+ 版本 `-Z` 参数) | 不支持 | | 后台下载 | 需配合 nohup | 原生支持 `-b` | | 输出方式 | 默认输出到 stdout | 默认保存为文件 | | 重定向处理 | 需手动加 `-L` | 自动跟随 | | 库集成 | libcurl 可嵌入应用程序 | 无库形式,无法嵌入 | ### 协议支持:为什么 cURL 能做 wget 做不了的事 cURL 支持超过 20 种协议,这意味着它不仅是 HTTP 客户端,还能发送邮件、操作 LDAP 目录、通过 SFTP/SCP 传输文件: ```bash # 通过 SMTP 发送邮件 curl smtp://mail.example.com --mail-from sender@test.com --mail-rcpt receiver@test.com -T mail.txt # SFTP 上传文件(wget 完全不支持上传) curl -T backup.tar.gz sftp://user@host.example.com/backup/ # 查询 LDAP 目录 curl ldap://ldap.example.com/cn=admin,dc=example,dc=com ``` wget 只支持 HTTP/HTTPS/FTP 三种协议。这不是偷懒,而是设计上的取舍——专注做好下载这一件事。 ### 递归下载:wget 的杀手级特性 递归下载是 wget 独有的能力,也是它和 cURL 最大的功能分界线: ```bash # 镜像整个网站(最常用的递归下载场景) wget --mirror --convert-links --adjust-extension --page-requisites --no-parent https://example.com/docs/ # 从文件列表批量下载 wget -i download-urls.txt # 限定深度递归 wget --recursive --level=2 --no-parent https://example.com/data/ ``` cURL 无法递归下载。如果你需要对网站做离线备份、数据抓取或批量归档,wget 是唯一的选择。 ### API 交互:cURL 的主场 cURL 在 API 开发测试领域几乎是事实标准。大多数 API 文档的示例代码都用 cURL 编写,这不是偶然: ```bash # 发送 JSON 请求 curl -X POST https://api.example.com/users -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9" -d '{"name":"test","role":"admin"}' # 只看响应头(调试必备) curl -I https://api.example.com/health # 提取 HTTP 状态码(脚本判断用) curl -s -o /dev/null -w "%{http_code}" https://api.example.com/status ``` wget 虽然也能发 POST 请求(`--post-data`),但不支持自定义请求方法、无法设置复杂 Header、不能方便地查看响应细节,在实际 API 调试中几乎不可用。 ### 底层架构差异:libcurl vs 独立程序 这是面试中能拉开差距的知识点。 **cURL 基于 libcurl 库**。libcurl 是一个成熟的跨平台 C 库,提供了稳定的 API 接口,几乎所有主流编程语言都有对应的绑定(Python 的 pycurl、PHP 的 curl 扩展、Node.js 的 node-libcurl 等)。这意味着你在代码中用这些库发 HTTP 请求时,底层跑的就是 libcurl,和命令行的 cURL 是同一套实现。 **wget 没有库形式**。它是一个独立的命令行程序,无法嵌入到应用程序中。如果你需要在代码中实现下载功能,要么自己写 HTTP 逻辑,要么用 libcurl。 ```bash # 验证:Python 的 requests 库底层不是 libcurl # 但 pycurl 是 libcurl 的 Python 绑定 import pycurl # 底层就是 libcurl ``` 这个架构差异也解释了为什么 Docker 官方镜像和大多数 CI/CD 环境预装的是 cURL 而不是 wget——因为很多构建工具依赖 libcurl。 ### 现代特性对比 cURL 在持续演进,许多现代特性 wget 尚不支持: | 特性 | cURL | wget | |------|------|------| | HTTP/2 | 支持(`--http2`) | 不支持 | | HTTP/3 (QUIC) | 支持(`--http3`) | 不支持 | | 并行传输 | 支持(`-Z`) | 不支持 | | 自动解压 | gzip/brotli/zstd | gzip | | SOCKS 代理 | SOCKS4/SOCKS5 | 不支持 | | 多种认证 | Basic/Digest/NTLM/AWS v4 | Basic/Digest | ### 生产环境中的常见坑 **坑 1:cURL 不自动跟随重定向** 很多 API 会返回 301/302 重定向,cURL 默认不会跟随,导致你以为请求失败了: ```bash # 错误:返回 301 但不跳转 curl https://example.com/old-api # 正确:加 -L 跟随重定向 curl -L https://example.com/old-api ``` wget 默认自动跟随重定向,不需要额外参数。 **坑 2:wget 下载大文件磁盘不够** wget 默认行为是直接把响应写入文件,如果磁盘空间不足会下载失败且留下不完整的文件: ```bash # 先检查文件大小再决定是否下载 wget --spider https://example.com/huge-file.zip 2>&1 | grep Length ``` **坑 3:cURL 在脚本中不检查 HTTP 错误码** cURL 默认不会因为 4xx/5xx 而返回非零退出码,这在脚本中容易漏掉错误: ```bash # 错误:脚本不会因 404 而中断 curl https://api.example.com/not-exist # 正确:加 --fail 让 4xx/5xx 返回非零退出码 curl --fail https://api.example.com/not-exist ``` **坑 4:Docker 构建中用 wget 代替 cURL** 某些精简 Docker 镜像没有预装 cURL,有人会改用 wget 测试连通性。但 wget 的输出格式不同,且不支持 `-I`(只看头部),推荐在 Dockerfile 中安装 cURL: ```dockerfile RUN apt-get update && apt-get install -y curl --no-install-recommends ``` ### 面试回答模板 **简短版(30 秒):** cURL 是通用网络传输工具,支持 20+ 协议,基于 libcurl 库可嵌入应用,擅长 API 交互和双向数据传输;wget 是专注文件下载的工具,支持递归下载和网站镜像,擅长批量下载和后台任务。选 cURL 做接口调试和脚本自动化,选 wget 做文件下载和站点归档。 **详细版(2 分钟):** 在简短版基础上补充三点:一是底层架构差异,cURL 基于 libcurl 库可被程序调用,wget 是独立程序无法嵌入;二是现代特性,cURL 已支持 HTTP/2、HTTP/3 和并行传输,wget 在协议层面更新较慢;三是常见陷阱,比如 cURL 不自动跟随重定向需要加 `-L`,以及 `--fail` 参数在脚本中的重要性。 ### 选择决策速查 | 场景 | 推荐 | 原因 | |------|------|------| | API 开发调试 | cURL | 方法/头部/认证灵活可控 | | 简单文件下载 | 两者皆可 | wget 更直觉,cURL 更可控 | | 网站离线镜像 | wget | 递归下载能力不可替代 | | 多协议传输 | cURL | 支持 SFTP/SCP/SMTP 等 | | 脚本自动化 | cURL | 输出灵活,适合管道处理 | | 批量下载 | wget | `-i` 参数支持从文件读 URL | | Docker/CI 环境 | cURL | 预装率高,libcurl 被广泛依赖 | | 后台无人值守下载 | wget | `-b` 原生后台支持 | 实际工作中两者都应掌握。cURL 更适合开发者的日常交互,wget 更适合运维的批量下载场景。如果只能装一个,选 cURL——它的能力范围更广,且 libcurl 是很多工具链的底层依赖。
服务端5月28日 01:52
cURL 如何配置和使用代理服务器?代理是 cURL 在企业网络和调试场景中的核心能力。掌握代理配置,才能在内网受限、抓包分析、隐私保护等场景下灵活使用 cURL。 ### 代理的工作原理 cURL 通过代理服务器中转请求,流程是:cURL → 代理服务器 → 目标服务器。HTTP 代理对 HTTP 请求直接转发,对 HTTPS 请求使用 CONNECT 方法建立隧道;SOCKS 代理则在传输层转发流量,不关心上层协议。 理解这点很重要:**HTTP 代理转发 HTTPS 时,代理看不到请求内容**,因为它只建立了一条加密隧道。这也是为什么 HTTPS 代理和 SOCKS5h 更适合隐私敏感场景。 ### 基本代理设置 ```bash # HTTP 代理 curl -x http://proxy.example.com:8080 https://api.example.com curl --proxy http://proxy.example.com:8080 https://api.example.com # HTTPS 代理(代理连接本身加密) curl -x https://proxy.example.com:443 https://api.example.com # SOCKS5 代理 curl -x socks5://proxy.example.com:1080 https://api.example.com # SOCKS5h(DNS 由代理服务器解析,防止 DNS 泄露) curl -x socks5h://proxy.example.com:1080 https://api.example.com ``` `-x` 和 `--proxy` 完全等价。socks5 和 socks5h 的区别在于 DNS 解析位置:前者由本地解析,后者由代理服务器解析。如果你在意隐私,始终优先用 socks5h。 ### 代理类型怎么选 | 代理类型 | 协议格式 | 核心区别 | 适用场景 | | -------- | -------- | -------- | -------- | | HTTP | http:// | 明文连接代理,HTTPS 请求走 CONNECT 隧道 | 企业缓存代理、简单转发 | | HTTPS | https:// | 到代理的连接加密 | 需要加密代理通信的场景 | | SOCKS4 | socks4:// | 仅 TCP 转发,无认证 | 旧系统兼容 | | SOCKS5 | socks5:// | 支持 UDP 和多种认证方式 | 通用代理 | | SOCKS5h | socks5h:// | DNS 由代理端解析 | 隐私保护、防 DNS 泄露 | 选型建议:日常开发用 HTTP 代理即可;需要 UDP 或认证选 SOCKS5;隐私场景选 SOCKS5h。 ### 代理认证 代理服务器通常需要身份验证。cURL 支持三种认证方式: ```bash # 方式一:在代理 URL 中嵌入凭据 curl -x http://user:password@proxy.example.com:8080 https://api.example.com # 方式二:用 -U 单独指定凭据 curl -x http://proxy.example.com:8080 -U user:password https://api.example.com # NTLM 认证(Windows 域环境常见) curl -x http://proxy.example.com:8080 --proxy-ntlm -U user:password https://api.example.com # Digest 认证 curl -x http://proxy.example.com:8080 --proxy-digest -U user:password https://api.example.com ``` 注意:`-U` 方式下密码会出现在进程列表中。在共享服务器上,优先用 `~/.curlrc` 或环境变量 `CURL_PROXY_USER` 来避免密码暴露。 ### 环境变量与持久化配置 每次手动指定代理很繁琐,cURL 会自动读取以下环境变量: ```bash # 设置代理环境变量 export http_proxy="http://proxy.example.com:8080" export https_proxy="http://proxy.example.com:8080" export no_proxy="localhost,127.0.0.1,.example.com" # 设置后直接使用,无需 -x 参数 curl https://api.example.com # 临时忽略环境变量 curl --noproxy "*" https://api.example.com ``` 小写变量名(http_proxy)是通用约定,大写(HTTP_PROXY)部分工具也会读取。`no_proxy` 指定不走代理的域名,支持通配符。 更持久的方案是写入配置文件: ```bash # ~/.curlrc proxy = "http://proxy.example.com:8080" proxy-user = "username:password" noproxy = "localhost,127.0.0.1" ``` 写入后所有 cURL 请求默认走代理。需要临时跳过时用 `curl -q`(忽略 .curlrc)或 `--noproxy`。 ### 代理绕过 并非所有请求都需要走代理。内网地址、本地服务应该直连: ```bash # 绕过指定域名 curl --noproxy "localhost,127.0.0.1,internal.example.com" \ -x http://proxy.example.com:8080 \ https://api.example.com # 绕过所有代理(直连) curl --noproxy "*" https://api.example.com ``` `--noproxy` 的值支持逗号分隔的域名列表,也支持 `.example.com` 这样的域名后缀匹配。 ### 代理调试技巧 代理不工作时,按以下步骤排查: ```bash # 第一步:查看完整的连接过程 curl -v -x http://proxy.example.com:8080 https://api.example.com 2>&1 | grep -i proxy # 第二步:测试代理本身是否可达 curl -v -x http://proxy.example.com:8080 http://www.google.com # 第三步:通过代理查看出口 IP(确认代理生效) curl -x http://proxy.example.com:8080 https://api.ipify.org ``` 如果 `-v` 输出中看到 `Connected to proxy.example.com` 说明到代理的连接成功;如果卡在 `Proxy auth required` 说明认证问题;如果完全没有 proxy 相关输出,检查环境变量是否被正确加载。 ### 高级配置 **代理隧道**:HTTP 代理默认用 CONNECT 方法为 HTTPS 建立隧道。某些旧代理不支持 CONNECT,可以显式指定: ```bash curl -x http://proxy.example.com:8080 --proxy-tunnel https://api.example.com ``` **自定义代理请求头**:某些代理会校验 User-Agent,可以通过 `--proxy-header` 添加: ```bash curl --proxy-header "User-Agent: MyApp/1.0" \ -x http://proxy.example.com:8080 \ https://api.example.com ``` **代理 TLS 配置**:HTTPS 代理可能要求特定的 TLS 版本或证书: ```bash # 指定 TLS 版本 curl -x https://proxy.example.com:443 --proxy-tlsv1.2 https://api.example.com # 指定代理 CA 证书 curl -x https://proxy.example.com:443 --proxy-cacert /path/to/proxy-ca.crt https://api.example.com # 跳过代理证书验证(仅调试用) curl -x https://proxy.example.com:443 --proxy-insecure https://api.example.com ``` ### 实战场景 **场景一:企业内网访问外部 API** 企业网络通常有统一出口代理,需要域账号认证: ```bash curl -x http://corporate-proxy.company.com:8080 \ --proxy-ntlm \ -U "COMPANY\\username:password" \ --noproxy "localhost,127.0.0.1,*.internal.company.com" \ https://api.github.com/user ``` 注意 Windows 域用户名中的反斜杠需要转义为 `\\`。`--noproxy` 确保内网地址不走代理。 **场景二:用 SSH 隧道做临时代理** 没有代理服务器时,可以借助远程机器创建 SOCKS 代理: ```bash # 先在本地建立 SSH 隧道(后台运行) ssh -D 1080 -f -C -q -N user@remote-server # 然后通过隧道访问 curl -x socks5h://localhost:1080 https://api.example.com ``` `-D 1080` 在本地 1080 端口开 SOCKS 代理,`-f -C -q -N` 让 SSH 在后台压缩静默运行。用完后 `kill` 对应 SSH 进程即可。 **场景三:配合抓包工具调试** Charles 或 Fiddler 本质上是 HTTP 代理,默认监听 8888 端口: ```bash curl -x http://localhost:8888 -k https://api.example.com ``` `-k` 跳过证书验证,因为抓包工具使用自签证书。调试 HTTPS 请求时这一步必不可少。 ### 常见问题 **代理连接超时** 默认超时可能太短,特别是跨地域代理。增加超时: ```bash curl -x http://proxy.example.com:8080 --connect-timeout 30 --max-time 60 https://api.example.com ``` **HTTPS 通过 HTTP 代理失败** 正常情况下 HTTP 代理会自动用 CONNECT 方法处理 HTTPS。如果失败,检查代理是否禁止了 CONNECT,或者显式启用隧道: ```bash curl -x http://proxy.example.com:8080 --proxy-tunnel https://api.example.com ``` **DNS 泄露** 使用 socks5 代理时,DNS 请求仍在本地发出,可能暴露访问意图。换成 socks5h 让代理端解析 DNS: ```bash curl -x socks5h://proxy.example.com:1080 https://api.example.com ``` **代理认证失败** 先确认认证类型。大多数代理用 Basic 认证,企业环境可能用 NTLM 或 Digest。用 `-v` 查看代理返回的 `Proxy-Authenticate` 头,确定认证方式后再加对应参数。
服务端5月28日 01:27
cURL 中 -d 和 --data 有什么区别?## -d 和 --data 是什么关系? `-d` 和 `--data` 是同一个参数的短格式和长格式,功能完全等价,没有任何行为差异。 ```bash # 下面两条命令完全等价 curl -d "name=value" https://api.example.com curl --data "name=value" https://api.example.com ``` cURL 的大多数参数都有这种短/长格式对应关系,比如 `-X` 和 `--request`、`-H` 和 `--header`。选择哪种写法纯属个人偏好:命令行简短操作用 `-d`,脚本中为了可读性用 `--data`。 ## cURL 有哪些数据发送方式? `-d` 只是 cURL 数据发送家族中的一个。不同参数对应不同的数据处理逻辑和 Content-Type: | 参数 | 数据处理方式 | 默认 Content-Type | 典型场景 | |------|------------|-------------------|----------| | `-d` / `--data` | 发送数据,`@` 读取文件并去除回车换行 | application/x-www-form-urlencoded | 表单提交、API 调用 | | `--data-ascii` | 与 `-d` 完全相同 | application/x-www-form-urlencoded | 明确语义时使用 | | `--data-binary` | 发送原始数据,`@` 读取文件不做任何处理 | 不自动设置 | 二进制文件、保留换行的文本 | | `--data-urlencode` | 自动对特殊字符做 URL 编码 | application/x-www-form-urlencoded | 参数含空格或特殊字符 | | `--data-raw` | 原样发送,`@` 不被解析为文件路径 | application/x-www-form-urlencoded | 数据内容本身含 `@` 符号 | | `-F` / `--form` | multipart/form-data 编码 | multipart/form-data | 文件上传、混合表单 | | `--json` | 发送 JSON,自动设置 Content-Type | application/json | REST API JSON 交互 | ## -d 和 -F 的核心区别是什么? 这是面试高频追问点。`-d` 发送的是整体编码的请求体,`-F` 发送的是分段编码的请求体: ```bash # -d:整个请求体作为一个 URL 编码字符串 curl -d "name=张三&age=25" https://api.example.com/form # 请求体:name=张三&age=25 # Content-Type: application/x-www-form-urlencoded # -F:每个字段单独编码,用 boundary 分隔 curl -F "name=张三" -F "file=@photo.jpg" https://api.example.com/upload # Content-Type: multipart/form-data; boundary=----WebKitFormBoundary... ``` 简单记忆:纯键值对用 `-d`,涉及文件上传用 `-F`。两者不能混用——如果同时指定,cURL 只会使用后出现的那个。 ## 如何发送 JSON 数据? 用 `-d` 配合手动设置 Content-Type,或者直接用 `--json`(cURL 7.82.0+): ```bash # 方式一:-d + 手动设置 Header(兼容性最好) curl -X POST https://api.example.com/users \ -H "Content-Type: application/json" \ -d '{"name":"张三","age":25}' # 方式二:--json(7.82.0+,自动设置 Content-Type) curl --json '{"name":"张三","age":25}' https://api.example.com/users ``` 注意:`-d` 不会自动转义或编码 JSON 中的特殊字符。如果 JSON 数据来自用户输入或文件,务必确保格式合法: ```bash # 从文件读取 JSON curl -X POST https://api.example.com/users \ -H "Content-Type: application/json" \ -d @payload.json ``` ## --data-binary 和 -d 读取文件有什么不同? 这是另一个容易踩坑的点。当用 `@` 从文件读取数据时,`-d` 会静默去除回车符和换行符,`--data-binary` 则原样发送: ```bash # 准备一个包含换行的文件 # data.txt 内容: # line1 # line2 # line3 # -d 读取文件:换行被去除,数据变成 "line1line2line3" curl -d @data.txt https://api.example.com # --data-binary 读取文件:换行完整保留 curl --data-binary @data.txt https://api.example.com ``` 发送 JSON、XML、二进制文件时,永远用 `--data-binary` 而不是 `-d`,否则数据可能被静默破坏。 ## --data-urlencode 和 --data-raw 什么时候用? ```bash # --data-urlencode:参数值含空格或特殊字符时 curl --data-urlencode "query=hello world!" https://api.example.com/search # 实际发送:query=hello%20world%21 # --data-raw:数据内容本身包含 @ 符号,不想被解释为文件路径 curl --data-raw "@channel notify" https://api.example.com/webhook # 发送的就是字面量 @channel notify,不会去找叫 channel 的文件 ``` ## 多个 -d 参数如何处理? 多次使用 `-d` 时,cURL 会用 `&` 将各段拼接起来: ```bash curl -d "name=张三" -d "email=zhangsan@example.com" https://api.example.com/form # 等价于 curl -d "name=张三&email=zhangsan@example.com" https://api.example.com/form ``` 也可以混合使用不同数据参数,但要注意各自的编码规则: ```bash curl -d "title=测试" \ --data-urlencode "content=Hello World!" \ --data-binary @attachment.pdf \ https://api.example.com/mixed ``` ## 常见踩坑点 **1. 使用 `-d` 就隐含了 POST 请求** `-d` 会自动将请求方法设为 POST,不需要额外写 `-X POST`。如果你同时写了 `-X GET -d "data"`,cURL 会发送一个带 body 的 GET 请求——这在大多数服务端会被忽略或拒绝。 **2. Content-Type 需要自己管** `-d` 默认设置 `application/x-www-form-urlencoded`。如果你要发 JSON 却忘记加 `-H "Content-Type: application/json"`,服务端可能按表单解析导致 400 错误。 **3. @ 符号的双重含义** `-d @filename` 读取文件内容,`-d @-` 从标准输入读取。如果你的数据本身包含 `@`,必须用 `--data-raw`。 **4. -d 和 -F 不能混用** 一个请求中只能选一种数据提交方式。如果两者都写了,cURL 只认后出现的那个,前面的会被静默忽略。