自分のメモrubyhttp/httpsリクエスト

いつもhttpリクエストでどういう実装するのか悩むのでコピペですぐ使えるようにする。
faradayを経験してみる


```
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/net/protocol.rb:44:in `connect_nonblock': SSL_connect returned=1 errno=0 state=SSLv3 read server hello A: sslv3 alert handshake failure (OpenSSL::SSL::SSLError)
```

* https://jsonplaceholder.typicode.com/
* https://jsonplaceholder.typicode.com/todos/1
* https://h5y1m141.github.io/step-up-javascript/doc/use_jsonplaceholder_with_chrome.html
* https://qiita.com/takano-h/items/dd10818eb7e09161bc29 これを経験してみる

# ruby version
```
$ ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19]
```

# rubyのマニュアルはどこにあるの？
ruby マニュアルで検索
https://docs.ruby-lang.org/ja/
https://docs.ruby-lang.org/ja/2.6.0/doc/index.html

httpはネットワークのところにあった。
https://docs.ruby-lang.org/ja/2.6.0/library/index.html
https://docs.ruby-lang.org/ja/2.6.0/library/net=2fhttp.html

# マニュアルに沿って試してみる
## 例1: GET して 表示するだけ。httpでアクセスしていると思う

```
$ cat test.rb
require 'net/http'
Net::HTTP.get_print 'jsonplaceholder.typicode.com', '/todos/1'

$ ruby test.rb
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
```

* Net::HTTP.get_printの機能を調べるにはどうするか？
 * https://docs.ruby-lang.org/ja/latest/class/Net=3a=3aHTTP.html
 * 非常にわかりづらい,ページした方の方に各メソッドのリンクがある

* URI.parseのマニュアルは？
 * https://docs.ruby-lang.org/ja/latest/class/URI.html#S_PARSE

## 例2: URI を使う
```
$ cat test.rb
require 'net/http'
require 'uri'
Net::HTTP.get_print URI.parse('http://jsonplaceholder.typicode.com/todos/1') # httpsにすると動かなかった

$ ruby test.rb
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
```

## 例3: より汎用的な例
```
require 'net/http'
require 'uri'

url = URI.parse('http://jsonplaceholder.typicode.com')
res = Net::HTTP.start(url.host, url.port) {|http|
  http.get('/todos/1')
}
puts res.body

$ ruby test.rb
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}
```
* http.start https://docs.ruby-lang.org/ja/latest/method/Net=3a=3aHTTP/s/start.html
* http.get https://docs.ruby-lang.org/ja/latest/method/Net=3a=3aHTTP/s/get.html

## 例4: 上の例よりさらに汎用的な例
```
require 'net/http'

url = URI.parse('http://www.example.com/index.html')
req = Net::HTTP::Get.new(url.path)
res = Net::HTTP.start(url.host, url.port) {|http|
  http.request(req)
}
puts res.body

結果は省略、動いた
```

* Net::HTTP::Get.new https://docs.ruby-lang.org/ja/latest/class/Net=3a=3aHTTP=3a=3aGet.html

## リクエストヘッダを指定する方法、portも変える
https://qiita.com/takano-h/items/dd10818eb7e09161bc29
https://qiita.com/mogulla3/items/a4bff2e569dfa7da1896

```
require "net/http"

uri = URI.parse("http://jsonplaceholder.typicode.com/todos/1")
http = Net::HTTP.new(uri.host, uri.port) # port指定したければuri.portをport番号に置き換える
# puts uri.port # httpなら80、httpsなら443がデフォルト値になる
http.use_ssl = uri.scheme === "https" # uri.schemeがhttp or httpsだと思われ。一致したらtrueをhttp.use_sslを代入する。一致しなければfalseを代入。trueだとsslを使用するという設定になる

headers = { "Content-Type" => "application/json" } # ヘッダーの設定
req = Net::HTTP::Get.new(uri.path)
req.initialize_http_header(headers)

response = http.request(req) # リクエストを投げる

puts response.code # status code
puts response.body # response body
```

* Net::HTTP.new https://docs.ruby-lang.org/ja/latest/class/Net=3a=3aHTTP.html#S_NEW

## 最終形、リクエストを投げて、JSONオブジェクトをHashへパースする
https://qiita.com/mogulla3/items/a4bff2e569dfa7da1896

```
require 'net/http'
require 'uri'
require 'json'
require 'logger'

# [ロガー]
# カレントディレクトリのwebapi.logというファイルに出力
# (DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN)
logger = Logger.new('./webapi.log')
logger.info("start")

# [クエリパラメータ]
# URI.encode_www_formを使って「application/x-www-form-urlencoded」形式の文字列に変換
# 文字列はURLエンコードされた形式に変換（半角スペースの"+"への変換等）
#
# （変換例）
# 'bar baz' => 'bar+baz'
# 'あ' => '%E3%81%82'
# params = URI.encode_www_form({ param1: 'foo', param2: 'bar baz' , param3: 'あ' })

# [URI]
# URI.parseは与えられたURIからURI::Genericのサブクラスのインスタンスを返す
# -> 今回はHTTPプロトコルなのでURI::HTTPクラスのインスタンスが返される
#
# オブジェクトからは以下のようにして構成要素を取得できる
# uri.scheme => 'http'
# uri.host   => 'mogulla3.com'
# uri.port   => 4567
# uri.path   => ''
# uri.query  => 'param1=foo&param2=bar+baz&param3=%E3%81%82'

# uri = URI.parse("http://mogulla3.com:4567?#{params}")
uri = URI.parse("http://jsonplaceholder.typicode.com/todos/1")

begin
  # [GETリクエスト]
  # Net::HTTP.startでHTTPセッションを開始する
  # 既にセッションが開始している場合はIOErrorが発生
  response = Net::HTTP.start(uri.host, uri.port) do |http|
    # Net::HTTP.open_timeout=で接続時に待つ最大秒数の設定をする
    # タイムアウト時はTimeoutError例外が発生
    http.open_timeout = 5

    # Net::HTTP.read_timeout=で読み込み1回でブロックして良い最大秒数の設定をする
    # デフォルトは60秒
    # タイムアウト時はTimeoutError例外が発生
    http.read_timeout = 10

    # Net::HTTP#getでレスポンスの取得
    # 返り値はNet::HTTPResponseのインスタンス
    http.get(uri.request_uri)
  end

  # [レスポンス処理]
  # 2xx系以外は失敗として終了することにする
  # ※ リダイレクト対応できると良いな..
  #
  # ステータスコードに応じてレスポンスのクラスが異なる
  # 1xx系 => Net::HTTPInformation
  # 2xx系 => Net::HTTPSuccess
  # 3xx系 => Net::HTTPRedirection
  # 4xx系 => Net::HTTPClientError
  # 5xx系 => Net::HTTPServerError
  case response
  # 2xx系
  when Net::HTTPSuccess
    # [JSONパース処理]
    # JSONオブジェクトをHashへパースする
    # JSON::ParserErrorが発生する可能性がある
    # {"userId"=>1, "id"=>1, "title"=>"delectus aut autem", "completed"=>false}
    # p JSON.parse(response.body)
    result = JSON.parse(response.body)
    p result
    p result["userId"]
  # 3xx系
  when Net::HTTPRedirection
    # リダイレクト先のレスポンスを取得する際は
    # response['Location']でリダイレクト先のURLを取得してリトライする必要がある
    logger.warn("Redirection: code=#{response.code} message=#{response.message}")
  else
    logger.error("HTTP ERROR: code=#{response.code} message=#{response.message}")
  end

# [エラーハンドリング]
# 各種処理で発生しうるエラーのハンドリング処理
# 各エラーごとにハンドリング処理が書けるようにrescue節は小さい単位で書く
# (ここでは全て同じ処理しか書いていない)
rescue IOError => e
  logger.error(e.message)
rescue TimeoutError => e
  logger.error(e.message)
rescue JSON::ParserError => e
  logger.error(e.message)
rescue => e
  logger.error(e.message)
ensure
  logger.info("end")
end
----------------
$ ruby test.rb
{"userId"=>1, "id"=>1, "title"=>"delectus aut autem", "completed"=>false}
1
```

* 返り値はNet::HTTPResponseのインスタンス
https://docs.ruby-lang.org/ja/latest/class/Net=3a=3aHTTPResponse.html
* logger https://docs.ruby-lang.org/ja/latest/class/Logger.html
* https://jsonplaceholder.typicode.com/todos/1 応答がないことがよくある

# httpsでGETする。postが必要になったらまた調査しよう
https://qiita.com/katu_/items/a116c261c8616fd01fca

```
require 'net/https'
require 'uri'
require 'json'
require 'logger'

# [ロガー]
# カレントディレクトリのwebapi.logというファイルに出力
# (DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN)
logger = Logger.new('./webapi.log')
logger.info("start")

# [クエリパラメータ]
# URI.encode_www_formを使って「application/x-www-form-urlencoded」形式の文字列に変換
# 文字列はURLエンコードされた形式に変換（半角スペースの"+"への変換等）
#
# （変換例）
# 'bar baz' => 'bar+baz'
# 'あ' => '%E3%81%82'
# params = URI.encode_www_form({ param1: 'foo', param2: 'bar baz' , param3: 'あ' })

# [URI]
# URI.parseは与えられたURIからURI::Genericのサブクラスのインスタンスを返す
# -> 今回はHTTPプロトコルなのでURI::HTTPクラスのインスタンスが返される
#
# オブジェクトからは以下のようにして構成要素を取得できる
# uri.scheme => 'http'
# uri.host   => 'mogulla3.com'
# uri.port   => 4567
# uri.path   => ''AA
# uri.query  => 'param1=foo&param2=bar+baz&param3=%E3%81%82'
uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")

begin
  # [GETリクエスト]
  # Net::HTTP.startでHTTPセッションを開始する
  # 既にセッションが開始している場合はIOErrorが発生
  # httpのようにブロックを使ったら動かなかった
  # HTTP.startは使えない。startを使うと、すぐにsessionを貼るので、https.use_ssl=falseができない
  https = Net::HTTP.new(uri.host, uri.port)
  https.use_ssl = uri.scheme === "https"
  https.verify_mode = OpenSSL::SSL::VERIFY_NONE # テストのときはこれでよい。社内テストでエラーのときは対応が必要。ssl証明書を取得してあげる？
  # Net::HTTP.open_timeout=で接続時に待つ最大秒数の設定をする
  # タイムアウト時はTimeoutError例外が発生
  https.open_timeout = 5

  # Net::HTTP.read_timeout=で読み込み1回でブロックして良い最大秒数の設定をする
  # デフォルトは60秒
  # タイムアウト時はTimeoutError例外が発生
  https.read_timeout = 10

  req = Net::HTTP::Get.new(uri.request_uri)
  response = https.request(req)
  # [レスポンス処理]
  # 2xx系以外は失敗として終了することにする
  # ※ リダイレクト対応できると良いな..
  #
  # ステータスコードに応じてレスポンスのクラスが異なる
  # 1xx系 => Net::HTTPInformation
  # 2xx系 => Net::HTTPSuccess
  # 3xx系 => Net::HTTPRedirection
  # 4xx系 => Net::HTTPClientError
  # 5xx系 => Net::HTTPServerError

  case response
  # 2xx系
  when Net::HTTPSuccess
    # [JSONパース処理]
    # JSONオブジェクトをHashへパースする
    # JSON::ParserErrorが発生する可能性がある
    # {"userId"=>1, "id"=>1, "title"=>"delectus aut autem", "completed"=>false}
    # p JSON.parse(response.body)
    result = JSON.parse(response.body)
    p result
    p resul["userId"]
  # 3xx系
  when Net::HTTPRedirection
    puts "baaa"
    # リダイレクト先のレスポンスを取得する際は
    # response['Location']でリダイレクト先のURLを取得してリトライする必要がある
    logger.warn("Redirection: code=#{response.code} message=#{response.message}")
  else
    logger.error("HTTP ERROR: code=#{response.code} message=#{response.message}")
  end

# [エラーハンドリング]
# 各種処理で発生しうるエラーのハンドリング処理
# 各エラーごとにハンドリング処理が書けるようにrescue節は小さい単位で書く
# (ここでは全て同じ処理しか書いていない)
rescue IOError => e
  logger.error(e.class)
  logger.error(e.message)
  logger.error(e.backtrace)
rescue TimeoutError => e
  logger.error(e.class)
  logger.error(e.message)
  logger.error(e.backtrace)
rescue JSON::ParserError => e
  logger.error(e.class)
  logger.error(e.message)
  logger.error(e.backtrace)
rescue => e
  logger.error(e.class)
  logger.error(e.message)
  logger.error(e.backtrace)
ensure
  logger.info("end")
end
```

* どうやったらレスポンスがわかるのだろうか？
* https://search.yahoo.co.jp/search?p=ruby+%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9&fr=top_ga1_sa&ei=UTF-8&ts=1390&aq=-1&oq=&at=&ai=549c6186-a9e1-4671-a24e-85507603d2fd

## 今度これをやってみよう
https://qiita.com/toshihirock/items/19fc868d3c4c52411aa9
postがわかる気がする


# faraday
https://qiita.com/YumaInaura/items/fb18e3d56dced19cbd28
http://nekorails.hatenablog.com/entry/2018/09/28/152745
使いこなせなかった

```
require 'faraday'
require 'json'
require 'logger'
# gem install faraday が必要

# シンプル1
# p Faraday.get("https://jsonplaceholder.typicode.com/todos/1", ssl: { verify: false })

# シンプル2
# connection = Faraday.new("https://jsonplaceholder.typicode.com/todos/1", ssl: { verify: false })
# p connection.get

# シンプル３
# connection = Faraday.new(url: "https://jsonplaceholder.typicode.com/todos/1", ssl: { verify: false })
# p connection.get


# シンプル4
connection = Faraday.new(url: "https://jsonplaceholder.typicode.com",ssl: { verify: false })
response =  connection.get "/todos/1" do |request|
  request.headers["Content-Type"] = "application/json"
  # ssl を設定しようとしたがエラーになる
end

#p response
p response.body     # レスポンスbody
result = JSON.parse(response.body)
p result
p result["userId"]
#p response.headers  # レスポンスheader
#p response.status   # ステータスコード
#p response.success? # リクエストは成功か？(ステータスコードが200番台か？）
```

## エラーハンドリング
https://qiita.com/dany1468/items/2d5e18dee84225ede77d