LoginSignup
0
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-11-23

いつも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)

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

例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
}

例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

結果は省略、動いた

リクエストヘッダを指定する方法、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

最終形、リクエストを投げて、JSONオブジェクトをHashへパースする

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

httpsでGETする。postが必要になったらまた調査しよう

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://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

# シンプル3
# 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番台か?)

エラーハンドリング

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0