CI 里用 curl 检查接口时,可以保留错误响应体

17 次浏览4 条回复

有些 CI 里只写 curl -f,接口返回 400/500 时退出码有了,但响应体被丢掉,定位起来挺难。curl 7.76+ 可以这样:

curl --fail-with-body -sS \
  -o response.json \
  -w '\nHTTP %{http_code}\n' \
  https://example.com/health

--fail-with-body 遇到 400 及以上状态码仍返回非 0,但会把响应体留在 response.json;-sS 安静运行、出错时仍显示错误,-w 只打印状态行。

环境:curl 7.76+,Linux 或 macOS。真实项目里别把令牌直接写进命令或 CI 日志。

这个写法在 CI 里还有个小坑:命令因为 4xx/5xx 返回非 0 后,后续的 artifact 上传步骤可能不会执行。要保留 response.json,可以把上传配置成失败时也运行(比如 GitHub Actions 用 if: ${{ always() }}),否则响应体还是容易丢。另一个细节是 -o 会把成功响应也写进去,若只想保留失败内容,后面最好按 HTTP 状态码区分文件。

HosuLv1#1

这个写法在 CI 里还有个小坑:命令因为 4xx/5xx 返回非 0 后,后续的 artifact 上传步骤可能不会执行。要保留 response.json,可以把上传配置成失败时也运行(比如 GitHub Actions 用 if: ${{ always() }}),否则响应体还是容易丢。另一个细节是 -o 会把成功响应也写进去,若只想保留失败内容,后面最好按 HTTP 状态码区分文件。

对,别随手用 continue-on-error,它可能把探活失败变成整项成功。更稳一点是让 curl 步骤保留非 0,上传步骤用 if: ${{ always() && hashFiles('response.json') != '' }};这样既能拿到响应体,也不会把真正的检查失败吞掉。

NiroLv1#2

对,别随手用 continue-on-error,它可能把探活失败变成整项成功。更稳一点是让 curl 步骤保留非 0,上传步骤用 if: ${{ always() && hashFiles('response.json') != '' }};这样既能拿到响应体,也不会把真正的检查失败吞掉。

这个边界确实容易踩。还可以把“检查失败”和“上传失败”拆开:让 curl 步骤自然返回非 0,artifact 步骤只负责收集文件,别用 continue-on-error 把失败吞掉。Actions 里可以写成 if: ${{ always() && !cancelled() && hashFiles('response.json') != '' }},手动取消时不再继续上传。

另外,错误响应不一定是 JSON,也可能是 HTML 或纯文本;文件名叫 .body,后面按 Content-Type 再决定是否解析,会比固定叫 response.json 稳一点。

FinleyLv1#3

这个边界确实容易踩。还可以把“检查失败”和“上传失败”拆开:让 curl 步骤自然返回非 0,artifact 步骤只负责收集文件,别用 continue-on-error 把失败吞掉。Actions 里可以写成 if: ${{ always() && !cancelled() && hashFiles('response.json') != '' }},手动取消时不再继续上传。

另外,错误响应不一定是 JSON,也可能是 HTML 或纯文本;文件名叫 .body,后面按 Content-Type 再决定是否解析,会比固定叫 response.json 稳一点。

.body 这个建议很稳。再补一个 CI 细节:把 curl 的退出码和响应元信息也留一份,后续就不用猜文件格式:

rc=0
curl --fail-with-body -sS -o response.body \
  -w '%{http_code} %{content_type}\n' > response.meta \
  https://example.com/health || rc=$?
printf 'curl_exit=%s\n' "$rc" >> response.meta
exit "$rc"

artifact 步骤收 response.body 和 response.meta,解析前先看 Content-Type。这样既保留失败状态,也不会把 HTML 错误页硬当 JSON。