【Rails】Faraday2.0を使う
Railsアプリケーションで外部APIを使う場合に、導入していた Faraday。v1
からv2
にアップデートしており、保守性を考慮して少し仕様が変わっていた。Faradayの使い方を備忘録として残しておく。
- Ruby:v3.0.1
- Ruby on Rails : v6.1.3.2
- Faraday : v2.3.0
Faradayとは
Faraday is an HTTP client library that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.
- HTTP クライアントライブラリである
- Net::HTTP やその他の多くのアダプターに対して共通のインターフェースを提供する
- Faraday 2.0より、net_httpのみを標準サポート
- それ以外のアダプタはGemfileの追加が必要
- Rack のようにミドルウェアの設定が可能
利用シーンとしては、Railsアプリケーションで外部のAPIなどにアクセスする場合などに用いられる。
v1
からv2
への更新内容は以下に記載されている。
インストール
gem 'faraday'
$ bundle install
faraday_middleware
もv1では利用していたが、v2では非推奨になった。
net_httpを使う場合、faraday
のみで十分になっているように思えた。
Faraday Connection
外部のAPIを使う場合、 Faraday::Connection
オブジェクトを生成することが推奨されている。
理由としては以下が挙げられる。
- 共通のリクエストヘッダーの設定が可能
- 'Content-Type' = 'application/json'の設定
- authorizationの設定
- APIのベースURLの設定が可能
- アダプタやミドルウェアの設定が可能
- loggerの設定
APIに関して基本的にJSONファイルでやり取りし、Bearer認証の場合、以下のミドルウェアの設定になる。
conn = Faraday.new('https://base-url.com/api/v1') do |conn| conn.request :json conn.response :json, parser_options: { symbolize_names: true } conn.authorization :Bearer, 'authentication-token' conn.request :instrumentation end
JSON Request Middleware
conn = Faraday.new(...) do |conn| conn.request :json end
JSON Response Middleware
conn = Faraday.new(...) do |conn| conn.response :json, **options end
- JSONのレスポンスボディをKey/Valueペアのハッシュにパースする
- JSON.parseのoptionsを設定可能
- ハッシュのキーを文字列ではなくシンボルにしたい場合
conn.response :json, parser_options: { symbolize_names: true }
Authentication Middleware
Bearer認証
conn = Faraday.new(...) do |conn| conn.request :authorization, 'Bearer', 'authentication-token' end
conn = Faraday.new(...) do |conn| conn.request :authorization, :basic, 'username', 'password' end
Basicのユーザー名とパスワードを自動的にBase64エンコードしてくれる。
Instrumentation Middleware
ログに関して、Logger Middlewareで標準出力にログを出力することができる。しかしながら、ログの整形が好みでなかったので、Instrumentation Middleware
を使うようにした。
ActiveSupport::Notifications.instrument
(ActiveSupportが提供するInstrumentation API)を利用してイベントを発行している。
conn = Faraday.new(...) do |conn| conn.request :instrumentation, name: 'custom_name', instrumenter: MyInstrumenter ... end
そのイベントをActiveSupport::Notifications.subscribe
を使って、サブスクライブすることでデータを取得することができ、ログを好きなように整形して吐き出すことができる。
ActiveSupport::Notifications.subscribe('request.faraday') do |name, starts, ends, _, env| url = env[:url] http_method = env[:method].to_s.upcase request_body = env[:response].env.request_body duration = ends - starts response_body = env[:response].body status_logger = ActiveSupport::Logger.new('log/api-connection.log', 'daily') status_logger.formatter = proc do |severity, time, progname, msg| "[%s#%d] %5s -- %s: %s\n" % [format_datetime(time), $$, severity, progname, msg.force_encoding("UTF-8")] end status_logger.level = Rails.logger.level status_logger.info "#{env[:status]} #{http_method} '#{url}' #{request_body} #{duration}seconds data: #{response_body}" end
日本語文字化け対応で、force_encoding("UTF-8")追加している。
GET,POST,PUT,PATCH,DELETE
- get(url, params = nil, headers = nil)
- post(url, body = nil, headers = nil)
- put(url, body = nil, headers = nil)
- patch(url, body = nil, headers = nil)
- delete(url, params = nil, headers = nil)
response = connection.get('users', { page: 1, perPage: 10 }) # => GET https://base-url.com/api/v1/users?page=1&perPage=10 response = connection.post('item', { name: 'hoge', price: 100 }) # => POST https://base-url.com/api/v1/item if response.success? response.body[:id] end
注意事項
- urlの先頭にはスラッシュを入れない
- 先頭にスラッシュを入れると、絶対パスと認識され、Faraday Connectionで設定したベースURLがドメイン部分のみ適応される。
- https://base-url.dom/api/v1としていても、ドメイン以下の
api/v1
は勝手に破棄されてしまう。 - 先頭にスラッシュがない場合、相対パスとして認識される。
POST 'application/x-www-form-urlencoded'
上記で作成したFaraday ConnectionはJSON形式なので、application/x-www-form-urlencoded
にする場合は、以下のようにconn.request :url_encoded
を指定する。
connection = Faraday.new('https://base-url.com/api/v1') do |conn| conn.request :url_encoded conn.response :json, parser_options: { symbolize_names: true } conn.authorization :Bearer, 'authentication-token' conn.request :instrumentation end response = connection.post('item', { name: 'hoge', price: 100 })
Tokenを上書きする場合
Tokenを上書きする場合は、以下のように直接上書きすればいい。
connection.headers['Authorization'] = "Bearer #{token}"
まとめ
改めて、FaradayのDocumentを読んでみて、とても便利だと思えた。Raise Error Middlewareも確かに便利であるが、モジュールの中ではなく、begin ~ rescue ~ end
とraise
で処理を明示的に書くようにした。