今回は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パス: ディレクトリ名(フォルダ名)
作成した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.new
でFaraday::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.get
、post
などのトップレベルのショートカットメソッドは、単純なデフォルトの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パラメータは最近追加されたため、一部のアダプタでは部分的なサポートしかない場合があります(つまり、ブロックにはchunk
とoverall_received_bytes
のみが渡されます)。
参考
今回はHTTPクライアントライブラリであるFaradayについて紹介しました。外部APIとHTTP通信する実装の候補の1つになればと思います。