猫Rails

ねこー🐈

Faradayの使い方 59のレシピ

第1章 Faradayをはじめよう

001 Faradayとは?

🐱 今日はFaradayっていうHTTPクライアントのgemを紹介させてね。

👦🏻 ふぁらでい?

🐱 FaradayはRackミドルウェアに似たミドルウェア機能が特徴で、ミドルウェアを追加することで色々な機能を追加できるようになるよ。

👦🏻 らっく?みどるうぇあ?

🐱 Rackやミドルウェアについてはおいおい説明していくよ。とりあえず使ってみよう(๑´ڡ`๑)

002 セットアップ

🐱 まずはGemfileにfaradayを追加してね。

gem 'faraday'

🐱 次にbundle installコマンドを実行すると、RailsアプリケーションでFaradayが使えるようになるよ。

$ bundle install

🐱 Rails以外で使う場合はgem installすればOKだよ。

$ gem install faraday

003 使ってみよう

🐱 こんな感じでGETリクエストを送れるよ。

Faraday.get("http://example.com")

🐱 シンプルでわかりやすいね。

第2章 基本的な使い方

004 GETリクエスト

🐱 GETリクエストはこんな感じだよ。

Faraday.get("http://example.com")

005 POSTリクエスト

🐱 POSTリクエストはこんな感じだよ。

# 第二引数はパラメータ
Faraday.post("http://example.com", name: "chibi")

006 PATCHリクエスト

🐱 PATCHリクエストはこんな感じだよ。

# 第二引数はパラメータ
Faraday.patch("http://example.com", name: "chibi")

007 DELETEリクエスト

🐱 DELETEリクエストはこんな感じだよ。

Faraday.delete("http://example.com")

🐱 他にもPUTリクエストやHEADリクエスト等も同じように送れるよ。

008 コネクションを使う

🐱 こんな感じで、明示的にコネクションを生成してからリクエストすることもできるよ。

connection = Faraday.new("http://example.com")
connection.get

🐱 Faraday::Connection.newでも同じ事ができるよ。

connection = Faraday::Connection.new("http://example.com")
connection.get

🐱 こんな感じでオプションを取ることができるよ。

connection = Faraday.new("http://example.com", params: {page: 1})
connection = Faraday.new("http://example.com", proxy: "http://proxy.com" )
connection = Faraday.new(url: "http://example.com") # urlはオプションで渡すこともできる

オプション一覧

オプション 解説
params パラメータ
url url
headers HTTPリクエストヘッダー
request リクエストのオプション
ssl SSLのオプション
proxy プロキシのオプション

🐱 他にもこんな感じのメソッドがあるよ。

connection.params     # パラメータ
connection.headers    # リクエストヘッダー
connection.ssl        # SSLのオプション
connection.url_prefix # URL
connection.proxy      # プロキシのオプション

connection.get        # GETリクエスト
connection.post       # POSTリクエスト
connection.patch      # PATCHリクエスト
connection.delete     # DELETEリクエスト

009 パラメータを指定する

🐱 第二引数でパラメータを指定できるよ。

Faraday.get("http://example.com/cats", page: 2)

🐱 GETの場合は直接クエリパラメータを指定してもOKだよ。

Faraday.get("http://example.com/cats?page=2")

🐱 ブロックでセットすることも出来るよ。

connection = Faraday.new("http://example.com")
connection.get "/cats" do |request|
  request.params[:page] = 2
end

🐱 こんな感じでセットすることも出来るよ。

connection = Faraday.new("http://example.com")
connection.params[:page] = 2
connection.get("/cats")

010 リクエストヘッダを指定する

🐱 第三引数でリクエストヘッダを指定できるよ。

Faraday.get("http://example.com/cats", {"page" => 2}, {"Accept" => "application/json"})

🐱 ブロックでセットすることも出来るよ。

connection = Faraday.new("http://example.com")
connection.get "/cats" do |request|
  request.headers["Accept"] = "application/json"
end

🐱 こんな感じでセットすることも出来るよ。

connection = Faraday.new("http://example.com")
connection.headers["Accept"] = "application/json"
connection.get("/cats")

011 レスポンスを使う

🐱 レスポンスはこんな感じで利用できるよ。

response = Faraday.get("http://example.com")

response.body     # レスポンスbody
response.headers  # レスポンスheader
response.status   # ステータスコード
response.success? # リクエストは成功か?(ステータスコードが200番台か?)

012 ミドルウェアを使う

🐱 Faradayにはミドルウェア機能があるよ。ミドルウェアを使うと、こんな感じでいろんな機能を追加できんだ。

# コネクション生成時にミドルウェアをセット
connection = Faraday.new("http://example.com") do |builder|
  builder.request  :url_encoded  # リクエストパラメータをURLエンコードする
  builder.response :logger       # リクエスト・レスポンスの内容を標準出力に出力する
  builder.adapter  :net_http     # net/httpをアダプタに使う
end

# GETリクエスト
connection.get("/cats")

🐱 更に踏み込んだ使い方については 第3章 Faradayミドルウェアを使うを見てね。

013 アダプタを使う

🐱 実はFaraday自体は実際にはHTTPリクエストをしないんだ。リクエスト処理はNet::HTTPやHTTPClientなどの他のライブラリに任せているんだよ。これらをアダプタと言うよ。アダプタを切り替えることで、FaradayのAPI(メソッド)は同じまま、リクエスト部分のライブラリを変えることができるよ。

🐱 使いたいアダプタをinstallしておいてね。今回はアダプタにHTTPClientを使うよ。

# Gemfile
gem 'faraday'
gem 'httpclient'
$ bundle install

🐱 こんな感じで使ってね。

connection = Faraday.new("http://example.com") do |builder|
  builder.adapter :httpclient # アダプタにHTTPClientを利用
end

🐱 利用できるアダプタはこんな感じだよ。

アダプタ シンボル
Net::HTTP :net_http
Net::HTTP::Persistent :net_http_persistent
Typhoeus :typhoeus
Patron :patron
EM-Synchrony :em_synchrony
EM-HTTP :em_http
Excon :excon
HTTPClient :httpclient

🐱 ちなみにデフォルトのアダプタはNet::HTTPだよ。並列リクエストとか特別なことをする場合以外は、Net::HTTPのままでいいと思うよ。

🐱 ここで2つ注意点があるよ。

🐱 1つ目はミドルウェアの追加時にアダプタは最後に置くこと。Faradayミドルウェアでは後から追加したミドルウェアが一番内側にくるんだ。アダプタは一番内側に来てほしいから、一番最後に指定する必要があるよ。

client = Faraday.new("http://example.com") do |builder|
  # ミドルウェア色々
  builder.request :url_encoded
  builder.response :logger
  ...

  # アダプタは一番最後
  builder.adapter :httpclient
end

🐱 2つ目はミドルウェアを指定する際に、アダプタ指定が必須なこと。デフォルトのミドルウェアとして:url_encoded:net_httpの2つが指定されているんだけど、ミドルウェアを指定する際にデフォルトのミドルウェアは無効になるんだ。だから自分で明示的に指定する必要があるよ。忘れがちだから気をつけてね。

# bad
# これだとアダプタが未選択になってしまう。
client = Faraday.new("http://example.com") do |builder|
  builder.request :url_encoded
  builder.response :logger
end

# good
client = Faraday.new("http://example.com") do |builder|
  builder.request :url_encoded
  builder.response :logger

  # アダプタ指定は必須。デフォルトのNet::HTTPを使う場合は`Faraday.default_adapter`か`:net_http`を指定する
  builder.adapter Faraday.default_adapter
end

014 アダプタの動作をカスタマイズする

🐱 Faradayはアダプタ間のAPIの違いを吸収してくれるから、各アダプタの使い方は気にせずにFaradayの使い方だけ覚えておけばいいよ。でもたまにアダプタ固有の機能を使いたくなることがあるよ。その場合にはブロックを使えば、アダプタを直接カスタマイズすることができるよ。

Net::Http

connection = Faraday.new("http://example.com") do |builder|
  builder.adapter :net_http do |http| # yields Net::HTTP
    http.idle_timeout = 100
  end
end

NetHttpPersistent

connection = Faraday.new("http://example.com") do |builder|
  builder.adapter :net_http_persistent do |http| # yields Net::HTTP::Persistent
    http.idle_timeout = 100
    http.retry_change_requests = true
  end
end

Patron

connection = Faraday.new("http://example.com") do |builder|
  builder.adapter :patron do |session| # yields Patron::Session
    session.max_redirects = 10
  end
end

HTTPClient

connection = Faraday.new("http://example.com") do |builder|
  builder.adapter :httpclient do |client| # yields HTTPClient
    client.keep_alive_timeout = 20
    client.ssl_config.timeout = 25
  end
end

015 JSONをPOSTする

🐱 JSONをPOSTするにはこんな感じだよ。

connection = Faraday.new("http://example.com")
connection.post("/cats.json") do |connection|
  # Content-Typeを指定
  connection.headers["Content-Type"] = "application/json"
  # bodyにJSON文字列を指定
  connection.body = {cat: {name: "tama"}}.to_json
end

🐱 ミドルウェアを使えばもっと簡単にできるよ。

connection = Faraday.new("http://example.com") do |builder|
  # jsonミドルウェアを指定すると、いい感じにやってくれる。
  builder.request :json

  builder.adapter Faraday.default_adapter
end

connection.post("/cats.json", {cat: {name: "tama"}})

016 SSLを使う

🐱 faradayではSSLを使う時に、認証局の証明書を指定する必要があるよ。ほとんどの場合、既にシステムにバンドルされているからそれを指定してあげてね。

🐱 ubuntuの場合は証明書のPATHはopensslコマンドで調べられるよ。

$ openssl version -d
OPENSSLDIR: "/usr/lib/ssl/certs"

🐱 PATHはsslオプションのca_pathで指定してね

connection = Faraday.new("https://example.com", ssl: { ca_path: "/usr/lib/ssl/certs" })

🐱 環境変数を使って指定することもできるよ。

ENV["SSL_CERT_PATH"] = "/usr/lib/ssl/certs"

connection = Faraday.new("https://example.com")

🐱 SSLエラーが出る場合は、こんな感じでSSLの認証をスキップできるよ。でもセキュリティー的に難ありだから、デバッグ以外では使わない方が良いと思うよ。

connection = Faraday.new("https://example.com", ssl: { verify: false })

017 並列処理をする

🐱 並列(parallel)で処理したい場合はアダプタにtyphoeusを使うといいよ。

# typhoeusのアダプタはtyphoeusライブラリにあるからrequireしてね
require "typhoeus"
require "typhoeus/adapters/faraday"

connection = Faraday.new("http://example.com") do |builder|
  # アダプタにtyphoeusを指定
  builder.adapter :typhoeus
end

response1, response2 = nil, nil

# 並列処理
connection.in_parallel do

  # この2つのリクエストは並列に実行されるよ
  response1 = connection.get("/1")
  response2 = connection.get("/2")

  # この時点ではリクエストは完了していないよ。そのためbodyはnilになるよ
  response1.body # => nil
  response2.body # => nil
end

# ブロック終了後にはリクエストは完了しているよ。そのためbodyやstatusにアクセスできるよ
response1.body # => 結果
response2.body # => 結果

🐱 参考 -> Webuilder240's Blog - FaradayでHTTPリクエストを並列で実行する方法

018 パラメータのシリアライズ方法を変更する

🐱 Faradayではデフォルトでクエリパラメータをids[]=1&ids[]=2のような形でシリアライズするよ。Railsでよく見る形だね。

# GET http://example.com/cats?ids[]=1&ids[]=2
Faraday.get("http://example.com/cats", ids: [1, 2])

🐱 これはパラメータエンコーダーにデフォルトのFaraday::NestedParamsEncoderが利用されているからだよ。明示的に書くとこうなるよ。

connection = Faraday.new("http://example.com/cats", request: { params_encoder: Faraday::NestedParamsEncoder })

# GET http://example.com/cats?ids[]=1&ids[]=2
connection.get { |request| request.params[:ids] = [1, 2] }

🐱 でも、どうもこの形はRailsでは標準だけどWeb全体の標準というわけではないらしいんだ。たとえばids=1&ids=2で送りたい場合がある。この場合はパラメータエンコーダーにFaraday::FlatParamsEncoderを利用すればいいよ。

connection = Faraday.new("http://example.com/cats", request: { params_encoder: Faraday::FlatParamsEncoder })

# GET http://example.com/cats?ids=1&ids=2
connection.get { |request| request.params[:ids] = [1, 2] }

🐱 こんな感じでコネクション毎に設定することもできるよ。

connection = Faraday.new("http://example.com/cats") do |builder|
  builder.request :url_encoded
  # optionsを通して設定する
  builder.options.params_encoder = Faraday::FlatParamsEncoder

  # アダプタ指定は必須
  builder.adapter Faraday.default_adapter
end

# GET http://example.com/cats?ids=1&ids=2
connection.get{ |request| request.params[:ids] = [1, 2] }

🐱 エンコーダーは自分で実装することもできるよ。その場合はencode(params)decode(query)を実装したクラスを用意してね。詳しくはFaraday パラメータのエンコードを差し替えてみるがとっても参考になるよ。

019 ファイルをアップロードする

🐱 ファイルのアップロードだよ。multipartミドルウェアを使って、ContentTypeをmultipart/form-dataにするのがポイントだよ。

connection = Faraday.new("http://example.com") do |builder|
  # `multipart`ミドルウェアを使って、ContentTypeをmultipart/form-dataにする
  builder.request :multipart
  builder.request :url_encoded

  builder.adapter Faraday.default_adapter
end

params = {
  # 画像ファイル
  picture: Faraday::UploadIO.new("cat.jpg", "image/jpeg")
}

connection.put("/foo.json", params)

020 プロキシを使う

🐱 Faradayでは内部的にURI::Generic#find_proxyを使って、環境変数http_proxyなどからプロキシを推測してくれるよ。

ENV['http_proxy'] = "http://proxy.com"

Faraday.get('http://www.example.com/')

🐱 ドキュメントはこれだよ。 -> https://docs.ruby-lang.org/ja/latest/method/URI=3a=3aGeneric/i/find_proxy.html

🐱 環境変数を無視したい場合はこうするよ。

Faraday.ignore_env_proxy = true

🐱 こんな感じでオプションで指定することもできるよ。

Faraday.new("http://www.example.com", proxy: "http://proxy.com")

Faraday.new("http://www.example.com", proxy: {
   uri: "http://proxy.example.com",
   user: "foo",
   password: "bar"
})

021 タイムアウトを指定する

🐱 リクエストのタイムアウトは、オプションのopen_timeouttimeoutで指定するよ。open_timeoutがコネクションを開くまでに待つ最大秒数で、timeoutがデータ読み込みまでに待つ最大秒数だよ。

connection = Faraday.new('http://example.com') do |builder|
  builder.options[:open_timeout] = 2 # コネクションを開くまでに待つ最大秒数
  builder.options[:timeout] = 5      # データ読み込みまでに待つ最大秒数

  builder.adapter Faraday.default_adapter
end

022 リクエストをスタブしてテストする

🐱 アダプタにFaraday::Adapter::Test::Stubsを使うことで、リクエストをスタブできるよ。

# スタブを生成する
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
  # 配列は`[ステータスコード, レスポンスヘッダ, レスポンスボディ]`
  stub.get("/a") { |env| [200, {}, "1"] }
end

# stubsを使って、コネクションを生成する
# さらに追加でスタブされたリクエストを追加している
connection = Faraday.new do |builder|
  builder.adapter :test, stubs do |stub|
    stub.get("b") { |env| [ 200, {}, "2" ] }
  end
end

# コネクション生成後にも、スタブされたリクエストを追加できる
# ここでは3つ目のスタブされたリクエストを追加してる
stubs.get("/c") { |env| [ 200, {}, "3" ] }

# GETリクエストをすると、指定したレスポンスが返される
connection.get("/a").body # => "1"
connection.get("/b").body # => "2"
connection.get("/c").body # => "3"
connection.get("/d")      # このpathはスタブがないのでエラーになる

🐱 Railsのコントローラーをテストする際はこんな感じになるよ。

コントローラー

class FoobarController < ApplicationController
  def index
    # コネクションはスタブ化しやすいようにprivateメソッドに切り出しておく
    connection.get("foo")

    render nothing: true
  end

  private
  def connection
    Faraday.new("http://www.example.com") do |connection|
      connection.request :url_encoded
      connection.response :logger

      connection.adapter Faraday.default_adapter
    end
  end
end

テスト

RSpec.describe FoobarController, type: :controller do

  # indexアクションのテスト
  describe "GET index" do

    # スタブ化されたコネクションを生成
    let!(:stub_connection) do
      Faraday.new do |connection|
        connection.adapter :test, Faraday::Adapter::Test::Stubs.new do |stub|
          stub.get("foo") do
            [ 200, {}, JSON.generate([ { id: 1, name: "Foo" }, { id: 2, name: "Bar" } ]) ]
          end
        end
      end
    end

    # リクエスト
    let(:request) { get :index, format: :json }

    # `controller.connection # => stub_connection`となるようにスタブ化して、リクエストを発行
    before do
      allow(controller).to receive(:connection).and_return(stub_connection)
      request
    end

    # リクエストが成功することをテスト
    it { expect(response).to be_success }
  end
end

🐱 参考 -> Faraday の スタブテスト

第3章 Faradayミドルウェアを使う

023 ミドルウェアを使う

🐱 Faradayはミドルウェアを追加することでいろんな機能を追加することができるよ。

# コネクション生成時にミドルウェアを追加
connection = Faraday.new("http://example.com") do |builder|
  builder.request  :url_encoded  # リクエストパラメータをURLエンコードする
  builder.response :logger       # リクエスト・レスポンスの内容を標準出力に出力する
  builder.adapter  :net_http     # net/httpをアダプタに使う
end

# GETリクエスト
connection.get("/cats.json")

024 2通りの書き方

🐱 ミドルウェアの追加方法は2つあるよ。シンボルを使う方法と、クラスを使う方法。どちらもやっていることは同じだけど、シンボル指定のほうがわかりやすいからシンボル指定をおすすめするよ。

1.シンボルを使う

connection = Faraday.new("http://example.com") do |builder|
  builder.request  :url_encoded
  builder.response :logger
  builder.adapter  :net_http
end

2.クラスを使う

connection = Faraday.new("http://example.com") do |builder|
  builder.use Faraday::Request::UrlEncoded
  builder.use Faraday::Response::Logger
  builder.use Faraday::Adapter::NetHttp
end

025 ミドルウェアにオプションを渡す

🐱 ミドルウェアにオプションを渡すには第2引数に指定してあげればOKだよ。

connection = Faraday.new do |builder|
  credentials = {
    :consumer_key    => consumer_key,
    :consumer_secret => consumer_secret,
    :token           => oauth_token,
    :token_secret    => oauth_token_secret
  }

  builder.request :oauth, credentials

  ...
end

🐱 参考 -> Ruby の HTTP クライアントライブラリ Faraday が便利そう

026 ミドルウェアを後から追加・削除する

🐱 ミドルウェアは後から追加・削除できるよ。

connection = Faraday.new

# ミドルウェアスタックの末尾(一番内側)に追加
connection.response :logger

# loggerミドルウェアを削除
connection.builder.delete(Faraday::Response::Logger)

# 0番目(ミドルウェアスタックの先頭)に追加
connection.builder.insert(0, Faraday::Response::Logger)

# 0番目のミドルウェアとloggerミドルウェアを置き換え
connection.builder.swap(0, Faraday::Response::Logger)

027 デフォルトのミドルウェアに気をつける

🐱 Faradayはデフォルトでurl_encoded(リクエストパラメータをURLエンコード)net_http(net_httpアダプタ)の2つのミドルウェアが指定されているよ。

# デフォルトではurl_encodedとnet_httpの2つのミドルウェアを利用する
Faraday.get("http://example.com")

# こんなイメージ
connection = Faraday.new do |builder|
  builder.request  :url_encoded
  builder.adapter  :net_http
end
connection.get("http://example.com")

🐱 ブロックでミドルウェアを指定するとデフォルトのミドルウェアは利用されないよ。アダプタの指定を忘れないようにね。

# bad
connection = Faraday.new do |builder|
  builder.request :multipart
end
connection.get("http://example.com") # アダプタ指定がないので、警告が出る

028 ミドルウェアの順番に気をつける

🐱 ミドルウェアは宣言する順番に気をつけてね。Rackミドルウェアと同じで、最初のミドルウェアが一番外側に来て、最後のミドルウェアが一番内側に来るよ。この順番を変えると動作が変わっちゃう場合があるから注意してね。特にアダプタは一番内側に来てほしいから、一番最後に持ってきてね。

Faraday.new(...) do |builder|
  # ミドルウェア順番を変えると動作が変わる場合がある
  builder.request :multipart
  builder.request :url_encoded

  # アダプタは一番内側に来てほしいので、一番最後
  builder.adapter :net_http
end

029 ミドルウェアの自作

🐱 Faradayミドルウェアは自作できるよ。Faraday::Middlewareを継承したクラスにcallメソッドを実装すれば自作ミドルウェアを作れるよ。

class MyMiddlewre < Faraday::Middleware
  def call(request_env)
    # リクエスト時の処理はここで行う
    # request_env[:method]などを使ってね
    #   - method: HTTPメソッド(:get, :post, ...)
    #   - url: リクエストURL(GETパラメータ含む)
    #   - body: POST/PUTパラメータ
    #   - request_headers: リクエストヘッダ

    @app.call(request_env).on_complete do |response_env|
      # レスポンス時の処理はここで行う
      # response_env[:status]などを使ってね
      #   - status: HTTPステータス
      #   - body: レスポンスボディ
      #   - response_headers: レスポンスヘッダ
    end
  end
end

🐱 レスポンス時の処理のみ実装したい場合はFaraday::Response::Middlewareon_completeを実装すれば簡単に自作ミドルウェアを作れるよ。

class MyMiddleware < Faraday::Response::Middleware
  def on_complete(response_env)
    # response時の処理はここで行う
  end
end

🐱 register_middlewareでミドルウェアを登録すれば、利用時にシンボルでアクセスできるようになるよ。

# ミドルウェアを登録
Faraday::Request.register_middleware my1: -> { MyMiddlewre1 }
Faraday::Response.register_middleware my2: -> { MyMiddlewre2 }
Faraday::Middleware.register_middleware my3: -> { MyMiddlewre3 }

# 利用
connection.request :my1
connection.response :my2
connection.use :my3

🐱 参考 -> Ruby の HTTP クライアントライブラリ Faraday が便利そう

第4章 Faradayミドルウェア一覧

030 Faradayミドルウェア一覧

🐱 Faradayミドルウェアはfaraday gemについてくるもの以外にも、別gemとして開発されているものがあるよ。特にfaraday_middlewareというgemには便利なミドルウェアが色々あるからおすすめだよ。

🐱 ここでは僕が便利そうだなーと思ったミドルウェアを、Gem別にまとめたよ。

faraday

タイプ クラス シンボル 解説
Request Faraday::Request::UrlEncoded :url_encoded リクエストパラメータをURLエンコードする
Request Faraday::Request::Multipart :multipart ファイルアップロード時にマルチパートでデータ送信する
Request Faraday::Request::BasicAuthentication :basic_auth ベーシック認証
Request Faraday::Request::TokenAuthentication :token_auth トークン認証
Request Faraday::Request::Authorization :authorization Authorizationヘッダーをセット
Request Faraday::Request::Retry :retry 失敗時にリトライする
Response Faraday::Response::Logger :logger リクエスト/レスポンス情報のログ吐き
Response Faraday::Response::RaiseError :raise_error 特定のステータスコードで、例外を投げる

faraday_middleware

タイプ クラス シンボル 解説
Request FaradayMiddleware::OAuth :oauth OAuth
Request FaradayMiddleware::OAuth2 :oauth2 OAuth2
Request FaradayMiddleware::EncodeJson :json リクエストボディをJSONエンコードする
Request FaradayMiddleware::MethodOverride :method_override X-Http-Method-Overrideヘッダを使い、POSTで他のHTTPメソッドを代用する
Response FaradayMiddleware::ParseJson :json パース(JSON)
Response FaradayMiddleware::ParseXml :xml パース(XML)
Response FaradayMiddleware::ParseYaml :yaml パース(YAML)
Response FaradayMiddleware::ParseMarshal :marshal パース(マーシャルデータ)
Response FaradayMiddleware::ParseDates :dates パース(時刻データ)
Response FaradayMiddleware::ParseJson::MimeTypeFix :json_fix パース(JSON) + MimeType修正
Response FaradayMiddleware::Mashify :mashify パース(Hashie::Mash)
Response FaradayMiddleware::Rashify :rashify パース(Hashie::Rash)
Response FaradayMiddleware::Chunked :chunked パース(チャンク転送のデータ)
Response FaradayMiddleware::Caching :caching レスポンスをキャッシュする
Response FaradayMiddleware::FollowRedirects :follow_redirects リダイレクト先をGETする
Middleware FaradayMiddleware::Gzip :gzip レスポンスbodyをGzip解凍する
Middleware FaradayMiddleware::Instrumentation :instrumentation ActiveSupport::Notificationsを使い、リクエストを計測する

faraday_middleware-parse_oj

タイプ クラス シンボル 解説
Response FaradayMiddleware::ParseOj :oj ojを使ってJSONをパース

faraday-cookie_jar

タイプ クラス シンボル 解説
Middleware Faraday::CookieJar :cookie_jar クッキーを扱う

faraday-detailed_logger

タイプ クラス シンボル 解説
Response Faraday::DetailedLogger :detailed_logger いい感じのログ

🐱 それぞれのミドルウェアの使い方については、以降のレシピで個別に解説していくよ。

031 Faraday::Request::UrlEncoded - リクエストパラメータをURLエンコードする

🐱 リクエストパラメータをURLエンコードして、Content-Typeをapplication/x-www-form-urlencodedにセットしてくれるよ。

builder.request :url_encoded

032 Faraday::Request::Multipart - ファイルアップロード時にマルチパートでデータ送信する

🐱 ContentTypeをmultipart/form-dataにして、マルチパートでデータ送信してくれるよ。

connection = Faraday.new("http://example.com") do |builder|
  # `multipart`ミドルウェアを使って、ContentTypeをmultipart/form-dataにする
  builder.request :multipart
  builder.request :url_encoded

  builder.adapter Faraday.default_adapter
end

params = {
  # 画像ファイル
  picture: Faraday::UploadIO.new("cat.jpg", "image/jpeg")
}

connection.put("/foo.json", params)

033 Faraday::Request::BasicAuthentication - ベーシック認証

🐱 Authorizationヘッダを使って、ベーシック認証ができるよ

builder.request :basic_auth, "username", "password"

🐱 ヘルパーメソッドでも同じことができるよ。

builder.basic_auth "username", "password"

034 Faraday::Request::TokenAuthentication - トークン認証

🐱 Tokenヘッダーを使って、トークン認証ができるよ

builder.request :token_auth, "token"

🐱 ヘルパーメソッドでも同じことができるよ。

builder.token_auth "token"

035 Faraday::Request::Authorization - Authorizationヘッダーをセット

🐱 Authorizationヘッダーを自分でセットできるよ。

# ベーシック認証を自分で実装
builder.request :authorization, :Basic, Base64.encode64("username" + ":" + "passwd")

🐱 ヘルパーメソッドでも同じことができるよ。

builder.authorization :Basic, Base64.encode64("username" + ":" + "passwd")

036 Faraday::Request::Retry - 失敗時にリトライする

🐱 失敗時にリトライするように設定できるよ。

builder.request :retry,
                max: 2,
                interval: 0.05,
                interval_randomness: 0.5,
                backoff_factor: 2,
                exceptions: [CustomException, "Timeout::Error"]

🐱 オプションはこんな感じだよ。

オプション 解説 デフォルト値
max 最大リトライ回数 2
interval リトライまでの待ち時間 0
interval_randomness リトライまでのランダム待ち時間。0~1のfloat 0
max_interval 最大待ち時間 Float::MAX
backoff_factor バックオフ(リトライ回数による待ち時間) 1
exceptions リトライする例外 [Errno::ETIMEDOUT, "Timeout::Error", Error::TimeoutError, Faraday::Error::RetriableResponse]
retry_if ブロックの戻り値がTrueの場合リトライ
faraday.request :retry, retry_if: ->(env, _exception) { !(400..499).include?(env.status) }
->(env,exception) { false }
methods retryが有効なHTTPメソッド [:delete, :get, :head, :options, :put]
retry_block リトライ後に任意の処理を実行
faraday.request :retry, retry_block: ->(env, middleware_options, retries, exception) { do_something }
Proc.new {}

037 Faraday::Response::Logger - リクエスト/レスポンス情報のログ吐き

🐱 リクエスト/レスポンスの情報を出力してくれるよ。デフォルトでは標準出力を使うよ。

connection = Faraday.new("https://google.com") do |builder|
  builder.response :logger

  builder.adapter  Faraday.default_adapter
end

# このタイミングでログ出力
connection.get

出力内容

I, [2018-08-31T07:21:38.436765 #57129]  INFO -- request: GET https://google.com/
D, [2018-08-31T07:21:38.436920 #57129] DEBUG -- request: User-Agent: "Faraday v0.15.2"
I, [2018-08-31T07:21:38.595954 #57129]  INFO -- response: Status 301
D, [2018-08-31T07:21:38.596067 #57129] DEBUG -- response: location: "https://www.google.com/"
content-type: "text/html; charset=UTF-8"
date: "Thu, 30 Aug 2018 22:21:38 GMT"
expires: "Sat, 29 Sep 2018 22:21:38 GMT"
cache-control: "public, max-age=2592000"
server: "gws"
content-length: "220"
x-xss-protection: "1; mode=block"
x-frame-options: "SAMEORIGIN"
alt-svc: "quic=\":443\"; ma=2592000; v=\"44,43,39,35\""
connection: "close"

🐱 第二引数でカスタムのloggerを設定できるよ。

logger = Logger.new("logfile.log")
connection = Faraday.new("https://google.com") do |builder|
  builder.response :logger, logger

  builder.adapter  Faraday.default_adapter
end

connection.get

038 Faraday::Response::RaiseError - 特定のステータスコードで、例外を投げる

🐱 ステータスコードが400番台、500番台の時に例外を投げるように設定できるよ。

builder.response :raise_error

投げられる例外は以下の通りだよ

ステータスコード 例外
404 Faraday::Error::ResourceNotFound
407 Faraday::Error::ConnectionFailed
400...600 Faraday::Error::ClientError

039 faraday_middlewareを使う

🐱 faraday_middlewareをインストールすると便利なミドルウェアが色々使えるようになるよ。

🐱 インストールはこんな感じだよ。

# Gemfile
gem 'faraday' # faraday_middlewareがfaradayに依存しているので、なくてもOK
gem 'faraday_middleware'
$ bundle install

🐱 使い方はFaraday gemのミドルウェアと同じだよ。

connection = Faraday.new("http://example.com") do |builder|
  builder.request  :url_encoded
  # Faraday gemのミドルウェアと同じように使える
  builder.response :json

  builder.adapter  :net_http
end

connection.get("/cats.json")

🐱 一部のミドルウェアは別のライブラリに依存しているから注意してね。

ミドルウェア 依存ライブラリ
FaradayMiddleware::Instrumentation activesupport
FaradayMiddleware::OAuth simple_oauth
FaradayMiddleware::ParseXml multi_xml
FaradayMiddleware::ParseYaml safe_yaml
FaradayMiddleware::Mashify hashie
FaradayMiddleware::Rashify rash_alt

040 FaradayMiddleware::OAuth - OAuth

🐱 OAuthを使うにはこのミドルウェアを使ってね。

041 FaradayMiddleware::OAuth2 - OAuth2

🐱 OAuth2を使うにはこのミドルウェアを使ってね。

# "token"がクエリパラーメータにaccess_tokenとして追加される
# "token"がAuthorizationリクエストヘッダに"Token token=<token_value>"として追加される
builder.request :oauth2, "token"

🐱 token_typeオプションも利用できるよ。

# "token"がAuthorizationリクエストヘッダに"Bearer <token_value>"として追加される
builder.request :oauth2, "token", token_type: :bearer

042 FaradayMiddleware::EncodeJson - リクエストボディをJSONエンコードする

🐱 JSON.dump()を使って、リクエストボディをJSON文字列にエンコードしてくれるよ。

builder.request :json

043 FaradayMiddleware::MethodOverride - X-Http-Method-Overrideヘッダを使い、POSTで他のHTTPメソッドを代用する

🐱 Railsアプリでよく見るやつだね。PATCHメソッドやDELETEメソッドが送れない時のために、DELETEなどのHTTPメソッドをX-Http-Method-OverrideヘッダとPOSTメソッドで代用するよ。Railsアプリ(Rackアプリ)では、デフォルトでHTTPメソッドをちゃんと解釈してくれるよ。

# 全てのHTTPメソッドを対象にする
builder.request :method_override

# PATCH、OPTIONSメソッドだけ対象にする
builder.request :method_override, rewrite: [:patch, :options]

044 FaradayMiddleware::ParseJson - パース(JSON)

🐱 レスポンスボディをJSON.parse()してくれるよ。

builder.response :json

🐱 content_typeオプションで、パースするレスポンスのContent-Typeを指定できるよ。これを指定しない場合、全てのレスポンスをJSONとしてパースしちゃうよ。

builder.response :json, :content_type => "application/json"

🐱 JSONでもContent-Typeがapplication/vnd.github.beta+jsonのような場合もあるから、正規表現で指定しておくと無難だよ。

# \b: 単語の境界
# $: 行末
builder.response :json, :content_type => /\bjson$/

🐱 parser_optionsを使うとパーサーにオプションを渡せるよ。

# `JSON.parse(response.body, symbolize_names: true)`のイメージ
builder.response :json, parser_options: { symbolize_names: true }

045 FaradayMiddleware::ParseXml - パース(XML)

🐱 レスポンスボディをXMLとしてパースするよ。multi_xmlに依存してるよ。

builder.response :xml, :content_type => /\bxml$/

046 FaradayMiddleware::ParseYaml - パース(YAML)

🐱 レスポンスボディをYAMLとしてパースするよ。

builder.response :yaml

047 FaradayMiddleware::ParseMarshal - パース(マーシャルデータ)

🐱 マーシャル化されたRubyオブジェクトにMarshal.load()を使うよ。

builder.response :marshal

048 FaradayMiddleware::ParseDates - パース(時刻データ)

🐱 時刻データをTime.parse()を使ってパースするよ。

builder.response :dates

049 FaradayMiddleware::ParseJson::MimeTypeFix - パース(JSON) + MimeType修正

🐱 レスポンスbodyがjsonっぽかったら、レスポンスheaderのContent-Typeを"application/json"に書き換えるよ。JSONなのに"text/javascript"とかでリクエストを返すようなAPIに利用するよ。

builder.response :json_fix

050 FaradayMiddleware::Mashify - パース(Hashie::Mash)

🐱 配列かハッシュの場合、Hashie::Mashにパースするよ。

builder.response :mashify

051 FaradayMiddleware::Rashify - パース(Hashie::Rash)

🐱 配列かハッシュの場合、Hashie::Rashにパースするよ。

builder.response :rashify

052 FaradayMiddleware::Chunked - パース(チャンク転送のデータ)

🐱 Transfer-Encoding: Chunkedのチャンク転送データを、元のデータにパースするよ。

builder.response :chuncked

053 FaradayMiddleware::Caching - レスポンスをキャッシュする

🐱 レスポンスをキャッシュできるよ。キャッシュストアにはActiveSupport::Cache(Railsで使われているキャッシュ機能)を利用できるよ。

# キャッシュの保存先をファイルにしたいので、FileStoreを使う
store = ActiveSupport::Cache::FileStore.new("tmp")
builder.response :caching, store

🐱 ignore_paramsオプションを使うと、パラメータを無視して同一のキャッシュキーとして扱えるよ。

store = ActiveSupport::Cache::FileStore.new("tmp")
# nameパラメータを無視してキャッシュする
builder.response :caching, store, ignore_params: ["name"]

🐱 キャッシュストアは必要なメソッドを実装してればなんでもOKだよ。自作の参考 -> HTTP Request Response Caching Using Faraday: Part 1

054 FaradayMiddleware::FollowRedirects - リダイレクト先をGETする

🐱 リダイレクト先をGETするよ。

# http://facebook.com/aboutはhttps://www.facebook.com/facebookにリダイレクトするようになっているので、こちらのデータを取得できる。
connection = Faraday.new "http://facebook.com" do |builder|
  builder.response :follow_redirects
  builder.adapter :net_http
end

response = connection.get("/about")

🐱 第二引数でオプションを指定できるよ。

builder.response :follow_redirects, limit: 5

オプション一覧

オプション 解説 デフォルト値
limit リダイレクト回数 3
standards_compliant HTTP標準仕様に準拠する false
callback リダイレクト時のコールバック(procで指定) nil

🐱 クッキーを使いたい場合は、faraday-cookie_jar(別gem)も使うといいよ。

Faraday.new(url) do |builder|
  builder.response :follow_redirects
  builder.use :cookie_jar
  builder.adapter Faraday.default_adapter
end

055 FaradayMiddleware::Gzip - レスポンスbodyをGzip解凍する

🐱 レスポンスbodyをGzip解凍するよ。Ruby1.9以上でnet_httpアダプタを使う場合は不要だよ。

056 FaradayMiddleware::Instrumentation - ActiveSupport::Notificationsを使い、リクエストを計測する

🐱 内部でActiveSupport::Notifications.instrumentを利用してイベントを発行しているよ。それをActiveSupport::Notifications.subscribeを利用して、サブスクライブしてね。ActiveSupportのInstrumentation機能については、こちらのRailsガイドに詳しく載ってるよ。参考 -> Active Support の Instrumentation 機能

# コネクション生成
connection = Faraday.new("http://example.com") do |builder|
  builder.use :instrumentation
  builder.adapter Faraday.default_adapter
end

# サブスクライブ
# キーは"request.faraday"
ActiveSupport::Notifications.subscribe("request.faraday") do |name, start_time, end_time, _, env|
  puts end_time - start_time
end

# GET
connection.get # リクエスト時間が出力される

057 faraday_middleware-parse_oj: JSONのパースにojを使う

🐱 faraday_middleware-parse_ojはJSONのパースにojを使うgemだよ。ojはRuby標準添付のjsonライブラリよりも高速だと噂のJSONパーサーだよ。ojのリポジトリ

🐱 まずインストールしてね。

# Gemfile
gem 'faraday'
gem 'faraday_middleware-parse_oj'
$ bundle install

🐱 使い方は他のFaradayミドルウェアと同じだよ。

connection = Faraday.new do |builder|
  builder.response :oj
  builder.adapter Faraday.default_adapter
end

connection.get("http://example.com/some.json")

🐱 faraday-cookie_jarはクッキーを扱うgemだよ。

🐱 まずインストールしてね。

# Gemfile
gem 'faraday'
gem 'faraday-cookie_jar'
$ bundle install

🐱 使い方は他のFaradayミドルウェアと同じだよ。

connection = Faraday.new("http://example.com") do |builder|
  builder.use :cookie_jar
  builder.adapter Faraday.default_adapter
end

connection.get "/one" # cookie取得
connection.get "/two" # cookie送信

059 faraday-detailed_logger: いい感じのログ

🐱 リクエスト/レスポンスの情報をいい感じに出力してくれるよ。curlっぽい出力になるよ。

🐱 まずはインストールしてね。

# Gemfile
gem 'faraday'
gem "faraday-detailed_logger"
$ bundle install

🐱 デフォルトでは標準出力にログを出力するよ。

connection = Faraday.new("https://google.com") do |builder|
  builder.response :detailed_logger

  builder.adapter  Faraday.default_adapter
end

# このタイミングでログ出力
connection.get

出力内容

I, [2018-09-27T19:37:05.334495 #38411]  INFO -- : GET https://google.com/
D, [2018-09-27T19:37:05.334773 #38411] DEBUG -- : "User-Agent: Faraday v0.15.2\n\n"
I, [2018-09-27T19:37:05.646647 #38411]  INFO -- : HTTP 301
D, [2018-09-27T19:37:05.646908 #38411] DEBUG -- : "location: https://www.google.com/\ncontent-type: text/html; charset=UTF-8\ndate: Thu, 27 Sep 2018 10:37:05 GMT\nexpires: Sat, 27 Oct 2018 10:37:05 GMT\ncache-control: public, max-age=2592000\nserver: gws\ncontent-length: 220\nx-xss-protection: 1; mode=block\nx-frame-options: SAMEORIGIN\nalt-svc: quic=\":443\"; ma=2592000; v=\"44,43,39,35\"\nconnection: close\n\n<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n<TITLE>301 Moved</TITLE></HEAD><BODY>\n<H1>301 Moved</H1>\nThe document has moved\n<A HREF=\"https://www.google.com/\">here</A>.\r\n</BODY></HTML>\r\n"

🐱 カスタムロガーを使う場合は第二引数に指定してね。

logger = Logger.new("logfile.log")

connection = Faraday.new("http://google.com") do |builder|
  builder.response :detailed_logger, logger
  builder.adapter  Faraday.default_adapter
end

🐱 Railsのloggerを使う場合はこんな感じになるよ。

builder.response :detailed_logger, Rails.logger

付録

Faradayの情報源

日本語の情報源

Web Clients for Ruby and What they should be in the future

🐱 RubyKaigi2016で@tkawaさんが行った、理想のWebクライアントについてのスライドと動画だよ。後半でWeb APIを個別のgemとして実装するのではなく、Faradayのミドルウェアとして実装する方法を紹介してるよ。

スライド 動画

Webuilder240's Blog - FaradayでHTTPリクエストを並列で実行する方法

🐱 typhoeusアダプタを使った並列リクエストのコードとベンチマークが解説されてるよ。

ローファイ日記 - Faradayの話 - OpenStack クライアント開発日記 (3)

🐱 Faradayの自作ミドルウェアについて、詳しく解説されているよ。

Ruby の HTTP Client「Faraday」を使った場合の例外の扱いとリトライ処理をどうするか考えてみた

🐱 Faradayの例外処理とリトライ処理について、かなり詳しく考察されてるよ。

Developers.IO - Faraday の スタブテスト

🐱 FaradayのスタブとRSpecを利用したテストの方法が解説されてるよ。

Faradayを使ったプログラムをRspecでテスト(Railsでない)

🐱 こちらもFaradayのスタブとRSpecを利用したテストの方法が解説されてるよ。

Apitore blog - RubyでAPIコールするならFaradayが簡単便利

🐱 oauth2を使ったコードが解説されてるよ。

Sarabande.jp - Ruby: Faraday を使って HTTP リクエストを送信する

🐱 Faradayの基本的な使い方が解説されているよ。

成らぬは人の為さぬなりけり - Faradayを触ってみた

🐱 Faradayの基本的な使い方から、ミドルウェアの自作まで解説されているよ。

Ruby の HTTP クライアントライブラリ Faraday が便利そう

🐱 FaradayとFaraday Middlewareについて解説されてるよ。

英語の情報源

faradayのGithubリポジトリ

🐱 faradayのGithubリポジトリだよ。wikiにも詳しい解説が載ってるよ。

faraday_middlewareのGithubリポジトリ

🐱 faraday_middlewareのGithubリポジトリだよ。wikiにも詳しい解説が載ってるよ。

faraday_middleware-parse_ojのGithubリポジトリ

🐱 faraday_middleware-parse_ojのGithubリポジトリだよ。

🐱 faraday-cookie_jarのGithubリポジトリだよ。

faraday-detailed_loggerのGithubリポジトリ

🐱 faraday-detailed_loggerのGithubリポジトリだよ。

HTTP Request Response Caching Using Faraday: Part 1

🐱 FaradayMiddleware::Cachingで自作のキャッシュストアを利用する方法を解説しているよ。