Faradayの使い方 59のレシピ
- 第1章 Faradayをはじめよう
- 第2章 基本的な使い方
- 第3章 Faradayミドルウェアを使う
- 第4章 Faradayミドルウェア一覧
- 030 Faradayミドルウェア一覧
- 031 Faraday::Request::UrlEncoded - リクエストパラメータをURLエンコードする
- 032 Faraday::Request::Multipart - ファイルアップロード時にマルチパートでデータ送信する
- 033 Faraday::Request::BasicAuthentication - ベーシック認証
- 034 Faraday::Request::TokenAuthentication - トークン認証
- 035 Faraday::Request::Authorization - Authorizationヘッダーをセット
- 036 Faraday::Request::Retry - 失敗時にリトライする
- 037 Faraday::Response::Logger - リクエスト/レスポンス情報のログ吐き
- 038 Faraday::Response::RaiseError - 特定のステータスコードで、例外を投げる
- 039 faraday_middlewareを使う
- 040 FaradayMiddleware::OAuth - OAuth
- 041 FaradayMiddleware::OAuth2 - OAuth2
- 042 FaradayMiddleware::EncodeJson - リクエストボディをJSONエンコードする
- 043 FaradayMiddleware::MethodOverride - X-Http-Method-Overrideヘッダを使い、POSTで他のHTTPメソッドを代用する
- 044 FaradayMiddleware::ParseJson - パース(JSON)
- 045 FaradayMiddleware::ParseXml - パース(XML)
- 046 FaradayMiddleware::ParseYaml - パース(YAML)
- 047 FaradayMiddleware::ParseMarshal - パース(マーシャルデータ)
- 048 FaradayMiddleware::ParseDates - パース(時刻データ)
- 049 FaradayMiddleware::ParseJson::MimeTypeFix - パース(JSON) + MimeType修正
- 050 FaradayMiddleware::Mashify - パース(Hashie::Mash)
- 051 FaradayMiddleware::Rashify - パース(Hashie::Rash)
- 052 FaradayMiddleware::Chunked - パース(チャンク転送のデータ)
- 053 FaradayMiddleware::Caching - レスポンスをキャッシュする
- 054 FaradayMiddleware::FollowRedirects - リダイレクト先をGETする
- 055 FaradayMiddleware::Gzip - レスポンスbodyをGzip解凍する
- 056 FaradayMiddleware::Instrumentation - ActiveSupport::Notificationsを使い、リクエストを計測する
- 057 faraday_middleware-parse_oj: JSONのパースにojを使う
- 058 faraday-cookie_jar: クッキーを扱う
- 059 faraday-detailed_logger: いい感じのログ
- 付録
- Faradayの情報源
- 日本語の情報源
- Web Clients for Ruby and What they should be in the future
- Webuilder240's Blog - FaradayでHTTPリクエストを並列で実行する方法
- ローファイ日記 - Faradayの話 - OpenStack クライアント開発日記 (3)
- Ruby の HTTP Client「Faraday」を使った場合の例外の扱いとリトライ処理をどうするか考えてみた
- Developers.IO - Faraday の スタブテスト
- Faradayを使ったプログラムをRspecでテスト(Railsでない)
- Apitore blog - RubyでAPIコールするならFaradayが簡単便利
- Sarabande.jp - Ruby: Faraday を使って HTTP リクエストを送信する
- 成らぬは人の為さぬなりけり - Faradayを触ってみた
- Ruby の HTTP クライアントライブラリ Faraday が便利そう
- 英語の情報源
- 日本語の情報源
- Faradayの情報源
第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_timeout
とtimeout
で指定するよ。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::Middleware
でon_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")
058 faraday-cookie_jar: クッキーを扱う
🐱 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リポジトリだよ。
miyagawa/faraday-cookie_jarのGithubリポジトリ
🐱 faraday-cookie_jarのGithubリポジトリだよ。
faraday-detailed_loggerのGithubリポジトリ
🐱 faraday-detailed_loggerのGithubリポジトリだよ。
HTTP Request Response Caching Using Faraday: Part 1
🐱 FaradayMiddleware::Cachingで自作のキャッシュストアを利用する方法を解説しているよ。