curlコマンドでちょこっとHTTPリクエストを試すだけの記事

## はじめに

こんにちは！お久しぶりです。
「実行されるとアプリサーバーにJSONを送るだけのバッチ」を作りたい人です[^1]。

[^1]: 当記事は下記記事の続きですが、「Linuxとかシェルって何？」を知っていればこの記事からでも読めます！[【黒い画面恐怖症向け】Linuxとかシェルとかざっくり知りたいのでゆるく図解した](https://qiita.com/akane_kato/items/007409cc67190a6d1832)

先輩に実装方法を聞いたら「そんなんcurl一択やんけ」と言われたので、curlコマンドを調べて使ってバッチを作成しました！

その時に得た知識をまとめていきたいと思います。

この記事では、以下の順番でcurlの基礎をご紹介していきます。

- curlコマンドって何？
- curlの基本の使い方
- どんな動作するのか、さくっと試したい！（試せますぞ！）
- curlの後ろにオプションをつけると、いろいろなことができますぞ！


## curlコマンドって何？

[curlコマンド](https://curl.haxx.se/)は、[こちらの記事](https://qiita.com/bunty/items/758425773b2239feb9a7)で紹介されているように、**様々なプロトコルを使用してデータ転送を行う**ことができるコマンドです。

HTTPのGETやPOSTはもちろん、HTTPSやFTP, Telnet, SMTP, IMAP, POP3など様々な通信プロトコルをサポートしています。

最近の端末には最初から入っていて、ターミナルですぐにcurlコマンドを使用できる状態だと思いますが、入っていない場合は[インストールが必要です](https://qiita.com/fuwamaki/items/53bca48416030ed2c599)。

### 当記事での環境とバージョン
macOS Catalina
curl 7.64.1

```
$ curl --version
curl 7.64.1 (x86_64-apple-darwin19.0) libcurl/7.64.1 (SecureTransport) LibreSSL/2.8.3 zlib/1.2.11 nghttp2/1.39.2
Release-Date: 2019-03-27
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp 
Features: AsynchDNS GSS-API HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz MultiSSL NTLM NTLM_WB SPNEGO SSL UnixSockets
```

バージョン情報を聞いただけでサポートしてるプロトコルとバージョンの特色も表示してくれるの、優しいですね！

今回はプロトコルの中でもHTTP通信のみ使用していきます！

## curlの基本の使い方
curlコマンドの基本形は下記の通りです。

```
$ curl {URL}
```

これで、URLのコンテンツをコンソールに表示させることができます。

試しにGoogleをcurlしてみましょう。

```
$ curl https://www.google.com/

<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head><meta content="&#19990;…
…中略…
…google.pmc=JSON.parse(pmc);})();</script></body></html>
```

Googleをcurlで取得すると、このようにひどく長いHTMLが取得できると思います。

Googleのレスポンス、長いし意味分かんないよ！ピンと来ないよ！という方は、[阿部寛](http://abehiroshi.la.coocan.jp/)さんのホームページを参考にしてみてはいかがでしょうか？

```
$ curl http://abehiroshi.la.coocan.jp/

<html>
<head>
…中略…
<title>阿部寛のホームページ</title>
</head>
  …中略…
  <body></body>
  …中略…
</html>
```

上述のようなシンプルで美しいHTMLが返ってこれば、上手く行っています！
（titleが文字化けしていても、通信自体はうまく行っているので大丈夫です！[阿部寛](http://abehiroshi.la.coocan.jp/)さんのホームページでF12/option+command+Iを押してみて、内容を見比べてみてください！）

もし下記のようなメッセージが出てくる場合、curlがデフォルトで入っていない可能性があるので、[インストールを試してみてください](https://qiita.com/fuwamaki/items/53bca48416030ed2c599)。

```
zsh: command not found: curl
```

## どんな動作するのか、さくっと試したい！（試せますぞ！）

さて、これからcurlコマンドを書いていくのですが、実際にアプリサーバと疎通した時にどういう返事が返ってくるのか、気になりませんか？

さくっと試してみるだけなら、Googleとか有名なサイトを少し見てみれば良いのですが、

「エラーが出たときはどんなものが返ってくるのか？」
「GETばかりしているけれど本当はPOSTしたいんだよね…[^2]」

こういう時に、いつまでもまるっと阿部さんに頼りきりではいけませんよね…

[^2]: GETとPOSTの違いは[こちらのQiita記事](https://qiita.com/Sekky0905/items/dff3d0da059d6f5bfabf)が分かりやすいです！

そんな時には[httpbin](http://httpbin.org/)がおすすめです！

<img width="1438" alt="スクリーンショット 2020-03-30 21.46.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/30b7ada7-877f-ec79-3e21-e4c72a09c561.png">


こちらは、HTTPリクエストをすると、いろいろなレスポンスを返してくれるWebサイトです。リクエストの書き方が正しいか、きちんとHTTP通信を行えるものなのかをさらりと確認できます！

また、URLを少し変えるだけで、GETやPOSTをそれぞれ行えたり、終了コードを恣意的に400や500にしたりすることもできます。curlに限らず、HTTP/HTTPS通信するコードだけぬるっと試してみたい時におすすめのWebサービスです。

この記事でも、しれっとhttpbinを使用していきます〜

## curlの後ろにオプションをつけると、いろいろなことができますぞ！

さて、今まではシンプル・オーソドックス・ビューティフルかつピュアなcurlコマンドで遊んできましたが、実はcurlの後ろには**オプション**を設定することができます！

これによってGETやPOSTを使い分けたり、ヘッダーやボディー、レスポンスを詳細に設定したり、エラーメッセージを表示させなかったりと、様々な詳細設定が可能となるのです。

### 【メソッド変更】curl -X {リクエストメソッド} “URL”

まずは、最もシンプルなリクエストメソッドの変更を試してみましょう。

Googleの例のように、何もオプションをつけないとGETメソッドとなりますが、**明示的にリクエストメソッドを指定したい**場合は[-X {リクエストメソッド} “URL”](https://curl.haxx.se/docs/manpage.html#-X)で変更することができます。

```
##########
# GETメソッド
# （curl http://httpbin.org/getと同じ結果になる）
##########

$ curl -X GET "http://httpbin.org/get"
                              
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.64.1", 
    "X-Amzn-Trace-Id": "Root=12345678901234567890"
  }, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "http://httpbin.org/get"
}
```

こちらはPOSTメソッドの例[^3]です。

[^3]: httpbinでは、デバッグ等に便利なようレスポンスにorigin等出してくれちゃうのですが、個人情報もあるため記事では伏せています！

```
##########
# POSTメソッド
##########

$ curl -X POST "http://httpbin.org/post" 
                                
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.64.1", 
    "X-Amzn-Trace-Id": "Root=12345678901234567890"
  }, 
  "json": null, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "http://httpbin.org/post"
}
```


### 【ヘッダ変更】curl -H “{パラメータ}”: “{値}” 
[-H “{パラメータ}”: “{値}”](https://curl.haxx.se/docs/manpage.html#-H) をつけることで、**HTTPヘッダを追加**することができます。
（ここからはPOSTメソッドで試していきます！）

```
$ curl -X POST "http://httpbin.org/post" -H "accept: application/json"

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.64.1", 
    "X-Amzn-Trace-Id": "Root=12345678901234567890"
  }, 
  "json": null, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "http://httpbin.org/post"
}
```

いかがでしょうか？

さっきのものとじっくり比べると、 “headers”内の “Accept”項目が変化しているのが分かりますね！

先ほどの-Xのみの例では　"Accept": "*/*"　だったのに対し、今回の-X -Hでは　Accept": "application/json"　となっています！

### 【コンテンツ変更】curl -X POST -d ‘{“パラメータ”}: {“値”}’ 
今度はいよいよPOSTメソッドでJSONを送ってみます！

HTTPヘッダに"content-type: application/json"を追加し、[POSTメソッドに-dオプション](https://curl.haxx.se/docs/manpage.html#-d)を付与することで、**POSTメソッドでJSON形式のデータをリクエストする**ことができます。

```
$ curl -H "content-type: application/json" -X POST -d'{"asa_gohan":"misosiru", "oyatsu":"karl"}' http://httpbin.org/post

{
  "args": {}, 
  "data": "{\"asa_gohan\":\"misosiru\", \"oyatsu\":\"karl\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "41", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.64.1", 
    "X-Amzn-Trace-Id": "Root=12345678901234567890"
  }, 
  "json": {
    "asa_gohan": "misosiru", 
    "oyatsu": "karl"
  }, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "http://httpbin.org/post"
}
```

朝ご飯といえば味噌汁、おやつといえばkarlですよね！

### 【通信詳細出力】curl -v
[-vオプション](https://curl.haxx.se/docs/manpage.html#-v)は**HTTP通信の詳細を出力する**ためのオプションです。

リクエストヘッダ、レスポンスヘッダとボディが見れるので、デバッグする時に付与すると便利です。

```
$ curl -v -H "content-type: application/json" -X POST -d'{"asa_gohan":"misosiru", "oyatsu":"karl"}' http://httpbin.org/post

*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to httpbin.org (xxx.xxx.xxx.xxx) port xx (xx)
> POST /post HTTP/1.1
> Host: httpbin.org
> User-Agent: curl/7.64.1
> Accept: */*
> content-type: application/json
> Content-Length: 41
> 
* upload completely sent off: 41 out of 41 bytes
< HTTP/1.1 200 OK
< Date: Mon, 30 Mar 2020 xx:xx:xx GMT
< Content-Type: application/json
< Content-Length: 486
< Connection: keep-alive
< Server: xxxxxxxx/xxxxxxxx
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
< 
{
  "args": {}, 
  "data": "{\"asa_gohan\":\"misosiru\", \"oyatsu\":\"karl\"}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "41", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/7.64.1", 
    "X-Amzn-Trace-Id": "Root=12345678901234567890"
  }, 
  "json": {
    "asa_gohan": "misosiru", 
    "oyatsu": "karl"
  }, 
  "origin": "xxx.xxx.xxx.xxx", 
  "url": "http://httpbin.org/post"
}
* Connection #0 to host httpbin.org left intact
* Closing connection 0
```

この最後の行の0は、Linuxコマンドの終了コードです。

Linuxコマンドは正常終了時は0、[異常終了時は1~255の正の整数](https://shellscript.sunone.me/exit_status.html)が返ってくるものなのですが、curlはHTTP通信時に400系エラーや500系エラーなどが出ても、終了コードが0になってしまいます。

あたかも成功したかのような顔をするタイプのコマンドです。いけすかないですね。

### 【終了コード変更】curl -f

エラーになってもしれっと正常終了する、ミス隠蔽マンcurlくんですが、[-fオプション](https://curl.haxx.se/docs/manpage.html#-f)を付ければ、異常終了時に終了コード22で終わってくれます！やったー！

[httpbin](http://httpbin.org/)のStatus codesを使用します。

<img width="1430" alt="スクリーンショット 2020-03-30 23.25.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/868503c1-2b19-3fa6-1cec-b118cb607b37.png">



まずは-fオプションなしのエラー

```
##########
# 400
##########
$ curl -X POST "http://httpbin.org/status/400" -H "accept: text/plain"

# 何も出ない…成功したの？失敗したの？（本当は400エラーで失敗している）
# echo $?コマンドで、直前に実行したコマンドの終了コードを表示できます。

$ echo $?
0
```

ほら…すぐ嘘つくんだから…

しかし、-fを付与してあげると、きちんとエラーを吐いてくれます！

```
##########
# 400
##########
$ curl -f -X POST "http://httpbin.org/status/400" -H "accept: text/plain"
curl: (22) The requested URL returned error: 400 BAD REQUEST

# やったー！分かりやすい！
# echo $?コマンドで一応確認しておきます。

$ echo $?
22
```

ヤッターーーー！！！！

404でも500でも、curlのエラーコードとHTTPエラーコードが表示されるので、とっても分かりやすいです！

```
$ curl -f -X POST "http://httpbin.org/status/404" -H "accept: text/plain"                                                       
curl: (22) The requested URL returned error: 404 NOT FOUND


$ curl -f -X POST "http://httpbin.org/status/500" -H "accept: text/plain"
curl: (22) The requested URL returned error: 500 INTERNAL SERVER ERROR
```

ただ、curl -fのみではフェールセーフではなく、401や407エラーなど認証系エラーが無視される可能性があるという[公式ドキュメントの記述](https://curl.haxx.se/docs/manpage.html#-f)もあります。

そのため[curl -sSを追加してエラー時のみエラーメッセージを表示する](https://superuser.com/questions/590099/can-i-make-curl-fail-with-an-exitcode-different-than-0-if-the-http-status-code-i)方法もあるそうです！


## おわりに

curl難しい…！特に終了コードの概念はなかったので、新しいことだらけでびっくりしてました！

また、誤認等ございましたら、そっと教えていただけますと幸いです…！＞＜　よろしくお願いいたします。

ちなみに「実行されるとアプリサーバーにJSONを送るだけのバッチ」は無事に完成したので、前回の記事でご紹介いただいたラーメン屋さんで一杯やろうかと思います！

いっぱいコメントありがとうございました！5億杯食べてきますね〜