できない.dev

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 パーサは動かない。

よくある原因

  1. Content-Type を指定していない。
    curl 自身はボディの中身を検査しないので、JSON かどうかは判定してくれない。
  2. シェルによるクオートの差。
    Windows のコマンドプロンプトはシングルクオートを引用符として扱わないため、'{"name":"curl"}' がそのまま文字列として渡り、JSON が壊れる。
  3. -d @body.json を使っている。-d はファイルから読むときに改行・復帰・NULL バイトを取り除くため、整形済み JSON が 1 行に潰れる。
    JSON としては有効なので気づきにくいが、改行を保ちたい用途では問題になる。
  4. -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-binaryContent-Type: application/json / Accept: application/json の 2 つのヘッダー指定をまとめた短縮形である。

curl --json '{"name":"curl","count":1}' https://api.example.com/items

利用中の curl が対応しているかは版数で判断する。

curl --version | head -1

3. 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/items

4. 送信内容を目で確認する

curl -v --json '{"name":"curl"}' https://api.example.com/items

> で始まる行が curl の送ったリクエストである。Content-Type: application/json が出ているか、本文が壊れていないかをここで確認する。
ヘッダーが正しいのに 4xx が返るなら、原因は curl 側ではなくスキーマ側にある。

この記事は役立ちましたか?