Rails

【Rails】HTTPクライアントライブラリ「Faraday」の使い方まとめ

Rails

 

今回はHTTPクライアントライブラリであるFaradayの導入と使い方について簡単にまとめました。RubyにはHTTPクライアントライブラリとしてNet::HTTPが標準装備されていますが、Faradayを使用することでより柔軟に簡単に実装することができます。(Faradayは内部的にはデフォルトでNet::HTTPを使用しています)

Faradayとは

Faradayとは、RailsアプリケーションでHTTPリクエストを行うための人気のHTTPクライアントライブラリです。主には、Railsアプリケーションから外部のAPIにアクセスする際に使用します。

またFaradayはよくRuby on Railsフレームワークと一緒に使われるため、「Rails Faraday」とも呼ばれています。

Faradayのインストール方法

RailsアプリケーションのGemfileにFaradayを追加します。

gem 'faraday'
% bundle install

Faradayの使い方

Faraday.getを使用することでFaraday::Responseオブジェクトが返却されます。(Faraday::Responseはstatus/headers/bodyを持ったオブジェクトになります)

response = Faraday.get('http://httpbingo.org')

response.status
=> 200

response.headers
=> {"access-control-allow-credentials"=>"true",
 "access-control-allow-origin"=>"*",
...

response.body
=> "<!DOCTYPE html>\n<html>
...

Faraday Connection

サードパーティのサービスやAPIに統合する場合には、Faraday::Connectionの作成が推奨されています。Faraday.newを呼び出すことでFaraday::Connectionを作成することができます。

この接続オブジェクトは以下のような構成になっています。

  • デフォルトのリクエストヘッダとクエリパラメータ
  • プロキシやタイムアウトといったネットワーク設定
  • 共通URLのベースパス
  • Faraday adapter & middleware

BaseURLとは BaseURL = URLスキーム+:// + FQDN + / + Baseパス

  • URLスキーム: http または https
  • FQDN : www.yahoo.co.jp や、172.0.0.1、localhostなど
  • Baseパス: ディレクトリ名(フォルダ名)

参考: URL設計(BaseURL/Baseパスを意識しよう)

 

作成したFaraday::Connectionに対してHTTP動詞(get/post…)を実行します。

conn = Faraday.new(
  url: 'http://httpbingo.org',
  params: {param: '1'},
  headers: {'Content-Type' => 'application/json'}
)

response = conn.post('/post') do |req|
  req.params['limit'] = 100
  req.body = {query: 'chunky bacon'}.to_json
end
# => POST http://httpbingo.org/post?param=1&limit=100

GET, HEAD, DELETE, TRACE

Faradayは通常リクエストボディを含まない、以下のHTTP動詞をサポートしています。

  • get(url, params = nil, headers = nil)
  • head(url, params = nil, headers = nil)
  • delete(url, params = nil, headers = nil)
  • trace(url, params = nil, headers = nil)

 

リクエスト時にURIクエリパラメータやHTTPヘッダを指定することができます。

response = conn.get('get', { boom: 'zap' }, { 'User-Agent' => 'myapp' })
# => GET http://httpbingo.org/get?boom=zap

POST, PUT, PATCH

Faradayはbodyを持つHTTP動詞もサポートしています。クエリパラメータの代わりにリクエストボディを受け付けます。

  • post(url, body = nil, headers = nil)
  • put(url, body = nil, headers = nil)
  • patch(url, body = nil, headers = nil)
# POST 'application/x-www-form-urlencoded' content
response = conn.post('post', 'boom=zap')

# POST JSON content
response = conn.post('post', '{"boom": "zap"}',
  "Content-Type" => "application/json")

 

Faradayはデフォルトの接続に含まれているurl_encodedミドルウェアによって、key/valueのハッシュを適切なbodyに自動変換してくれます。

# POST 'application/x-www-form-urlencoded' content
response = conn.post('post', boom: 'zap')
# => POST 'boom=zap' to http://httpbingo.org/post

詳細なHTTPリクエスト

Faradayはリクエストを作成する際の長いスタイルをサポートしています。これは多くのデフォルトを変更する必要がある場合や、HTTPリクエストの詳細がメソッドの引数に従って変更される場合に便利です。各HTTP動詞ヘルパーは、送信前に変更できる Faraday::Requestを生成できます。

以下の例は、実際の検索クエリとしてJSONリクエストボディを受け入れる架空の検索エンドポイントを示しています。

response = conn.post('post') do |req|
  req.params['limit'] = 100
  req.headers['Content-Type'] = 'application/json'
  req.body = {query: 'chunky bacon'}.to_json
end
# => POST http://httpbingo.org/post?limit=100

アダプタ

アダプタは、HTTPリクエストを実際に実行する責任があります。デフォルトのアダプタは RubyのNet::HTTPを使用しますが、様々なアダプタが利用可能です。TyphoeusアダプタでFaradayを使用したい場合はアダプターの詳細をご覧ください。

ミドルウェア

Faradayは、リクエストを行うためにRackに着想を得たミドルウェアスタックを使用しています。Faradayの多くの機能は、カスタムミドルウェアによって開放されます。Faradayにはいくつかのミドルウェアが含まれており、他にも外部のgemに存在しています。

ミドルウェアが提供する機能は以下になります。

  • 認証
  • ディスクまたはメモリに応答をキャッシュする
  • クッキー ・リダイレクトの追跡
  • JSONのエンコーディング/デコーディング
  • ログ出力
  • リトライ

これらの機能を利用するには、Faraday.newFaraday::Connectionを作成し、適切なミドルウェアをブロック内で追加します。

require 'faraday'
require 'faraday/retry'

conn = Faraday.new('http://httpbingo.org') do |f|
  f.request :json # encode req bodies as JSON and automatically set the Content-Type header
  f.request :retry # retry transient failures
  f.response :json # decode response bodies as JSON
  f.adapter :net_http # adds the adapter to the connection, defaults to `Faraday.default_adapter`
end

# Sends a GET request with JSON body that will automatically retry in case of failure.
response = conn.get('get', boom: 'zap')

# response body is automatically decoded from JSON to a Ruby hash
response.body['args'] #=> {"boom"=>["zap"]}

デフォルトのコネクション、デフォルトのミドルウェア

Faradayでは、キー/バリューのハッシュボディを自動的にフォームボディにエンコードすると述べました。内部的にFaraday.getpostなどのトップレベルのショートカットメソッドは、単純なデフォルトのFaraday::Connectionを使用します。デフォルト接続に使用される唯一のミドルウェアは、:url_encodedで、これはフォームハッシュをエンコードします。また、デフォルトのアダプタも使用します。

なお、ミドルウェアを含む独自の接続を作成した場合、:url_encodedミドルウェアも含めない限り、フォームボディをエンコードしませんので注意してください。

リクエストのカスタマイズ

接続によって設定を行うことができ、リクエストごとに調整することも可能です。

# コネクション時
conn = Faraday.new('http://httpbingo.org', request: { timeout: 5 })
conn.get('/ip')
# リクエスト時
conn.get do |req|
  req.url '/ip'
  req.options.timeout = 5
end

 

コンテキストオプションを使用して、リクエストに任意のデータを注入することもできます。これは全てのミドルウェアでenv内で利用可能です。

conn.get do |req|
  req.url '/get'
  req.options.context = {
    foo: 'foo',
    bar: 'bar'
  }
end

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

パラメータを異なる値で複数回送信する必要がある場合があります。この場合、パラメータエンコーダを手動で設定する必要があり、接続単位またはリクエスト単位で行うことができます。これは、すべてのHTTP動詞に適用されます。

# コネクション時
conn = Faraday.new request: { params_encoder: Faraday::FlatParamsEncoder }
conn.get('', { roll: ['california', 'philadelphia'] })
# リクエスト時
conn.get do |req|
  req.options.params_encoder = Faraday::FlatParamsEncoder
  req.params = { roll: ['california', 'philadelphia'] }
end

カスタムシリアライザー

必要に応じて、カスタムエンコーダを作成することもできます。

Faradayのparams_encoderの値は、次のメソッドに応答する任意のオブジェクトである必要があります。

  • #encode(hash) #=> String
  • #decode(string) #=> Hash

エンコーダはFaradayがクエリ文字列を処理する方法と、POSTボディをシリアル化する方法の両方に影響を与えます。デフォルトのエンコーダは、Faraday::NestedParamsEncoderです。

パラメータの順序

デフォルトでは、パラメータは名前でソートされながらシリアル化されます。これは、キャッシュ管理を改善するために非常に便利であり、ほとんどのサーバーは実際にはパラメータの順序を気にしません。ただし、特定の順序でパラメータを必要とするサーバーとやり取りする場合があります。その場合、エンコーダを設定してソートをスキップすることができます。この設定は、デフォルトのFaraday::NestedParamsEncoderおよびFaraday::FlatParamsEncoderの両方でサポートされています。

Faraday::NestedParamsEncoder.sort_params = false
# or
Faraday::FlatParamsEncoder.sort_params = false

プロキシ

Faradayは、URI#find_proxyを使用して、システムからプロキシ設定を自動的に推測しようとします。これにより、http_proxy、ftp_proxy、no_proxyなどの環境変数から取得できます。何らかの理由でこの動作を無効にしたい場合は、グローバル変数ignore_env_proxyを設定することで行うことができます。

Faraday.ignore_env_proxy = true

 

コネクションを初期化する際に、カスタムプロキシを指定することもできます。

conn = Faraday.new('http://www.example.com', proxy: 'http://proxy.com')

ストリーミング応答

時にはストリーミングレスポンスを受け取る必要がある場合があります。これはリクエストオプションのon_dataを使用して行うことができます。

on_dataコールバックは、チャンク文字列とこれまでに受信したバイト数の合計のタプルを受け取ります。

# A buffer to store the streamed data
streamed = []

conn.get('/stream/10') do |req|
  # Set a callback which will receive tuples of chunk Strings,
  # the sum of characters received so far, and the response environment.
  # The latter will allow access to the response status, headers and reason, as well as the request info.
  req.options.on_data = Proc.new do |chunk, overall_received_bytes, env|
    puts "Received #{overall_received_bytes} characters"
    streamed << chunk
  end
end

# Joins all response chunks together
streamed.join

 

現時点では、on_dataストリーミングは一部のアダプタでのみサポートされています。どのアダプタがサポートされているかは、Awesome Faraday比較表を参照するか、アダプタのドキュメントを確認してください。さらに、envパラメータは最近追加されたため、一部のアダプタでは部分的なサポートしかない場合があります(つまり、ブロックにはchunkoverall_received_bytesのみが渡されます)。

参考

Faraday Usage

 

今回はHTTPクライアントライブラリであるFaradayについて紹介しました。外部APIとHTTP通信する実装の候補の1つになればと思います。