Ruby で WebAPI をコールするサンプルコード

# 概要

- Ruby で WebAPI をコールするサンプルコードを書いてみた
- 外部のライブラリを使わず、標準ライブラリのみ使っている (標準ライブラリの使い方を理解できるように)
- よく使いそうな要素を盛り込んだ (HTTP, HTTPS, GET, POST, JSON, XML, timeout, Basic access authentication, etc.)
- 動作確認環境: macOS Sierra + Ruby 2.4.0

# サンプルのソースコード

```
require 'json'
require 'net/https'
require 'rexml/document'
require 'uri'

class WebAPIClient

  def initialize()
  end

  def encode(params, encoding)
    return nil if params == nil
    encoded = {}
    params.map{|k,v|
      encoded[k.encode(encoding)] = v.encode(encoding)
    }
    return encoded
  end

  def to_query(params, enc=nil)
    if params != nil && !params.empty?
      return '?' + URI.encode_www_form(params, enc)
    else
      return ''
    end
  end

  def get_response(x)

    # 文字 エンコーディング
    if x['encoding'] != nil
      x['params'] = encode(x['params'], x['encoding'])
    end

    # URL エンコード
    if x['method'] == 'GET'
      url = x['base_url'] + to_query(x['params'])
    elsif x['method'] == 'POST'
      url = x['base_url']
    end

    # URI 文字列をパースして
    # URI::Generic のサブクラスのインスタンスを生成
    uri = URI.parse(url)

    # リクエスト情報を確認用に出力
    puts "URI#to_s: #{uri.to_s}"
    puts "URI#host: #{uri.host}"
    puts "URI#port: #{uri.port}"
    puts "URI#request_uri: #{uri.request_uri}"

    # Net::HTTP オブジェクトを生成
    http = Net::HTTP.new(uri.host, uri.port)

    # 接続時に待つ最大秒数
    http.open_timeout = 5

    # 読みこみ (read(2)) 一回でブロックしてよい最大秒数
    http.read_timeout = 5

    if uri.scheme == 'https'
      # HTTPS の場合に true を設定
      http.use_ssl = true
      # SSL 証明書の検証モード
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end

    # Net::HTTPRequest のサブクラスのインスタンスを生成
    if x['method'] == 'GET'
      req = Net::HTTP::Get.new(uri.request_uri, x['headers'])
    elsif x['method'] == 'POST'
      req = Net::HTTP::Post.new(uri.request_uri, x['headers'])
      req.set_form_data(x['params'])
    end

    # BASIC 認証
    if x['basic_auth'] != nil
      req.basic_auth(x['basic_auth']['account'], x['basic_auth']['password'])
    end

    # TCP コネクションを張り、HTTP セッションを開始
    # ブロックが終わったときに接続を自動的に閉じる
    res = http.start{|h|
      h.request(req)
    }

    # レスポンス情報を確認用に出力
    puts "Net::HTTPResponse#code: #{res.code}"
    puts "Net::HTTPResponse#message: #{res.message}"

    return res
  end

  def parse_json(json)
    return JSON.parse(json)
  end

  def parse_xml(xml)
    return REXML::Document.new(xml)
  end

  def self.invoke(x)

    client = WebAPIClient.new()
    res = client.get_response(x)

    if res.kind_of?(Net::HTTPSuccess)
      if res.content_type.include?('json')
        obj = client.parse_json(res.body)
      elsif res.content_type.include?('xml')
        obj = client.parse_xml(res.body)
      else
        obj = res.body
      end
      # レスポンスを出力
      puts obj
    end
  end

end

# HTTP, POST method, xml response
# ユーザーローカルWikipedia API: http://wikipedia.simpleapi.net/
WebAPIClient.invoke({
  'base_url' => 'http://wikipedia.simpleapi.net/api',
  'method' => 'POST',
  'params' => {
    'keyword' => '紅玉',
    'output' => 'xml'
  },
  'headers' => {
    'User-Agent' => 'Mozilla/5.0 (WebAPIClient; Ruby)'
  }
})

# HTTPS, GET method, JSON response
# Users | GitHub Developer Guide: https://developer.github.com/v3/users/
WebAPIClient.invoke({
  'base_url' => 'https://api.github.com/users/niwasawa',
  'method' => 'GET'
})
```

# 参考資料

- library json: https://docs.ruby-lang.org/ja/latest/library/json.html
- library net/http: https://docs.ruby-lang.org/ja/latest/library/net=2fhttp.html
- library rexml: https://docs.ruby-lang.org/ja/latest/library/rexml.html
- library uri: https://docs.ruby-lang.org/ja/latest/library/uri.html