Windows版curlでJSONをPOSTする際に困った話

## 症状
[Beebotte](https://beebotte.com/home)のREST APIを利用しようとして以下のコマンドを実行したところ、不正なリクエストとして処理されてしまうという現象が発生しました。

`curl https://api.beebotte.com/v1/data/publish/test/res?token={your_token} -i -H "Content-Type: application/json" -d '{"data":"hello"}'`

[Beebotte](https://beebotte.com/home)のREST APIのリクエスト形式は下記のURLから確認できますが、パラメータ等は問題ないはずです。

https://beebotte.com/docs/tokenauth

実際、上のコマンドはLinuxのシステム上で動作することを確認しました。
しかし、いざ実行してみると次のようなレスポンスを受信しました。

```
HTTP/1.1 400 Bad Request
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Type: application/json; charset=utf-8
Content-Length: 47
ETag: W/"2f-fWws/bQDrRp4ZzKF1y8mWQ"
Date: Sat, 11 Aug 2018 15:41:47 GMT
Connection: keep-alive

{"error":{"message":"Bad Request","code":1403}}
```
どうやら送信したパケットの内容がおかしいみたいです。
## 結論
結論から示すと以下のようにJSONのキーや値を囲む「"」を「\"」に置き換えることで解決します。
`curl https://api.beebotte.com/v1/data/publish/test/res?token={your_token} -i -H "Content-Type: application/json" -d '{\"data\":\"hello\"}'`

## 調査
WireSharkを利用して送信したパケットの中身を確認してみます。
「api.beebotte.com」との通信だけを確認したいのでフィルタリング用にIPアドレスを調べます。
`nslookup api.beebotte.com`
「api.beebotte.com」のIPアドレスは「54.221.205.80」であったので次の条件でパケットのフィルタリングを行います。
`ip.addr==54.221.205.80`
パケットのキャプチャを開始し、冒頭のcurlコマンドを実行すると次のようなパケットを確認できます。

![invalid_packet.JPG](https://qiita-image-store.s3.amazonaws.com/0/236173/68a0d725-9140-6ae1-88c1-e1c2318aa381.jpeg)

ここで、Linuxのシステム上でも同じ方法でパケットをキャプチャしたものが次になります。

![valid_packet.JPG](https://qiita-image-store.s3.amazonaws.com/0/236173/1fcdae0f-c29a-016a-315e-30bf91b25989.jpeg)

両者を比較してみると、Windowsのcurlで送信したパケットはJSONが「Line-based text data」となっているのに対して、Linuxのcurlで送信したパケットでは「JavaScript Object Notation」として格納されています。また、Windowsの方では、「data」や「hello」といったキー、値である文字列につけたはずの「"」が外されています。おそらく原因はこれでしょう。

検索してみた結果、これと同じ問題が[stackoverflow](https://stackoverflow.com/)に上がっていました。

curl PATCH - JSON is sent as Line-based text data
https://stackoverflow.com/questions/42049180/curl-patch-json-is-sent-as-line-based-text-data

上のページによれば、JSONのキーや値を囲む「"」の前に「\」を加えてエスケープすることで上手く動作するみたいです。
したがって、冒頭で登場した次のコマンドを

`curl https://api.beebotte.com/v1/data/publish/test/res?token={your_token} -i -H "Content-Type: application/json" -d '{"data":"hello"}'`

下記のように変更すれば良いです。

`curl https://api.beebotte.com/v1/data/publish/test/res?token={your_token} -i -H "Content-Type: application/json" -d '{\"data\":\"hello\"}'`

実行結果

```
HTTP/1.1 200 OK
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Vary: X-HTTP-Method-Override
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Content-Length: 4
ETag: W/"4-sya1BisvDmkEaBBxdTTLCQ"
Date: Sat, 11 Aug 2018 15:42:13 GMT
Connection: keep-alive

true
```
正常に動作することを確認しました。

## あとがき
この仕様は結構有名らしく、この記事を書いてる際に検索してみたら同じような記事が何件か見つかりましたが、記事を書く練習ということで最後まで書いてしまいました。
REST APIやWebHooksなどを利用する際にcurlは非常に便利ですが、WindowsとLinuxで使い方が少し異なるのは少し(時間的には大分)困りました。そもそもWindows上のコマンドプロンプトを使うことは、なるべく避けたほうがいいかもしれません。