curl で JSON を POST してもサーバー側で受け取れない
-d は既定で Content-Type: application/x-www-form-urlencoded を送るため、JSON を渡してもサーバーはフォーム送信として解釈する。
ヘッダーを明示するか、curl 7.82.0 以降なら --json を使う。
公開:
要約
curl -d '{"name":"curl"}' https://api.example.com/items が 400 や 415 で返る、あるいはサーバー側で本文が空に見えるとき、多くは Content-Type がフォーム送信のままになっていることが原因である。
-d(--data)はブラウザのフォーム送信を再現するオプションで、指定がなければ application/x-www-form-urlencoded を送る。
JSON を本文に入れても、ヘッダーがフォームのままならサーバー側の JSON パーサは動かない。
よくある原因
- Content-Type を指定していない。
curl 自身はボディの中身を検査しないので、JSON かどうかは判定してくれない。 - シェルによるクオートの差。
Windows のコマンドプロンプトはシングルクオートを引用符として扱わないため、'{"name":"curl"}'がそのまま文字列として渡り、JSON が壊れる。 -d @body.jsonを使っている。-dはファイルから読むときに改行・復帰・NULL バイトを取り除くため、整形済み JSON が 1 行に潰れる。
JSON としては有効なので気づきにくいが、改行を保ちたい用途では問題になる。-X POSTだけを書いてボディを渡していない。
メソッドは POST になるが本文は空である。
解決策
1. Content-Type を明示する
curl -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"curl","count":1}'-d を使うと curl は自動的に POST になるため、-X POST は省略できる。
2. curl 7.82.0 以降は --json を使う
--json は --data-binary と Content-Type: application/json / Accept: application/json の 2 つのヘッダー指定をまとめた短縮形である。
curl --json '{"name":"curl","count":1}' https://api.example.com/items利用中の curl が対応しているかは版数で判断する。
curl --version | head -13. Windows ではファイル渡しにする
クオートの解釈差を避けるには、本文をファイルに書いて @ で読ませるのが確実である。
curl --json @body.json https://api.example.com/items--json が使えない場合は --data-binary にヘッダーを添える。--data-binary は -d と違い改行や復帰をそのまま送る。
curl -H "Content-Type: application/json" --data-binary @body.json https://api.example.com/items4. 送信内容を目で確認する
curl -v --json '{"name":"curl"}' https://api.example.com/items> で始まる行が curl の送ったリクエストである。Content-Type: application/json が出ているか、本文が壊れていないかをここで確認する。
ヘッダーが正しいのに 4xx が返るなら、原因は curl 側ではなくスキーマ側にある。