Faradayを触ってみた
実はこれが、2014年最初のエントリー。サボりすぎ。
WebAPIを呼ぶスクリプト書くのにFaradayを使ってみたので、使い方などをメモっておこうと思います。
Faraday
自体は前から知ってたんですが、リダイレクトとかめんどくさい事考えなくて良い場合に、
特にHTTPクライアントのライブラリ入れて使うより、Net::HTTP
で十分じゃない?と思って使った事ありませんでした。
なんとなーく、Ruby書きたくなって、使ったことないgemを使ってみたかったので、使ってみた次第です。
ちなみに、読み方は ふぁらでい
なんでしょうかね?
環境
使用するライブラリのバージョン
gem 'faraday', '~> 0.9.0' gem 'sinatra', '~> 1.4.5' gem 'sinatra-contrib', '~> 1.4.2'
今回のコードはこちら
今回の流れ
- まずはWebAPI作っとく
- Faradayの基本的な使い方
- Middlewareを使ってみる
- Middlewareを作ってみる
まずはWebAPI作っとく
とりあえず、簡単にJSONでも返してくれるAPIが欲しいので、何でも良いのですが、
サーバサイドのデバッグもしてみたいので、自前で用意してみます。
といってもすっごい簡単な物で良いので、Sinatra
さんにご協力頂き、さくっと作ります。
require "sinatra" require "sinatra/reloader" if development? require "sinatra/json" get "/test" do json name: :hoge, age: 100 end post "/test" do json params end post "/test.json" do json JSON.parse(request.body.read) end
ポイント
reloder
入れとくと、コード変えた時に再起動の必要なくなります。(Sinatra::Contrib)- 単純に
require "json"
して{ key: value }.to_json
返しても良いんですが、それだけだとresponseのcontent-typeがtext/html
になるので、Sinatra::Contrib
のsinatra/json
を使いますprovides: :json
すれば良いのですが、めんどくさいので
Faradayの基本的な使い方
先程作ったSinatraアプリは起動しておきます。
GETしてみる
require "faraday" require "json" require "pp" res = Faraday.new(:url => "http://localhost:4567").get("/test") body = JSON.parse(res.body) pp body
※status:200以外の場合の事はまったく考えてません
とまぁ、こんな感じでget
する訳なんですが、これだと、Net::HTTP
でもたいして変わらないんですよね。
require "net/http" require "uri" require "json" require "pp" res = Net::HTTP.get URI("http://localhost:4567/test") body = JSON.parse res pp body
URLを分割してなくて良い分、こちらの方が楽かも?。 ただ、Faradayの良い所はこれだけじゃわからないので、先に進みます。
POSTしてみる
Faraday
client = Faraday.new(:url => "http://localhost:4567") res = client.post "/test", { hoge: 100, foo: :bar } body = JSON.parse res.body pp body
Net::HTTP
res = Net::HTTP.post_form URI("http://localhost:4567/test"), { hoge: 100, foo: :bar } body = JSON.parse res.body pp body
と、まぁ、ここまでもあまりメリット感じませんね。
Content-Type変えてみる
jsonをpostしてみます。
Faraday
client = Faraday.new(:url => "http://localhost:4567") res = client.post do |req| req.url '/test.json' req.headers['Content-Type'] = 'application/json' req.body = { hoge: 100, foo: :bar }.to_json end body = JSON.parse res.body pp body
Net::HTTP
uri = URI("http://localhost:4567/test") req = Net::HTTP::Post.new uri, initheader = {'Content-Type' =>'application/json'} req.body = { hoge: 100, foo: :bar }.to_json res = Net::HTTP.start(uri.host, uri.port, use_ssl: false) do |http| http.request req end body = JSON.parse res.body pp body
少し楽になりましたね。 実はもっと楽になります。
Middlewareを使ってみる
FaradayにはMiddleware
という仕組みがあります。(Rackと同じような仕組みになってる)
こいつを使う事で、拡張できるようになってます。
ここには、一番最初に書いてあるんですけどね。
request/responseをログ出力してみる
client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.response :logger faraday.adapter Faraday.default_adapter end res = client.get "/test" body = JSON.parse res.body pp body
ちなみにmiddleware使う場合、adapter
は必ず指定しないとエラーになるっぽい??です。
ソースまで追ってないですが。
出力結果
I, [2014-05-10T23:45:34.114079 #12029] INFO -- : get http://localhost:4567/test D, [2014-05-10T23:45:34.114147 #12029] DEBUG -- request: User-Agent: "Faraday v0.9.0" I, [2014-05-10T23:45:34.117054 #12029] INFO -- Status: 200 D, [2014-05-10T23:45:34.117099 #12029] DEBUG -- response: content-type: "application/json" content-length: "25" x-content-type-options: "nosniff" server: "WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)" date: "Sat, 10 May 2014 14:45:34 GMT" connection: "close" {"name"=>"hoge", "age"=>100}
こんな感じで出力されます。
FaradayMiddlewre
faraday_middleware
っていうgemが用意されていて、使えるmiddlewareが既に結構用意されています。
これを使うと、さっきのjsonパターンは数倍楽になります。
bundler使っている場合は、Gemfileに以下を追加してインストールしておきます。
gem 'faraday_middleware', '~> 0.9.1'
使ってみます。
require "faraday" require "faraday_middleware" require "json" require "pp" client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.request :json # ここ faraday.adapter Faraday.default_adapter end res = client.post "/test.json", { hoge: 100, foo: :bar } body = JSON.parse res.body pp body
middlewareでrequestのmiddlewareとして:json
を入れとくだけで
っていう事をやってくれます。
この辺でハマったこと
こんな事をするとContent-Type
がapplication/json
になりません。
client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.request :url_encoded faraday.request :json faraday.adapter Faraday.default_adapter end
この辺で、ちゃんとソース読んだ方が良さそうなので、見てみます。 まずは、
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L87
この辺。
request
にmiddlewareの設定を渡すと use_symbol
-> use
という流れでここに来ます。
@handlers
というインスタンス変数にpushしていってますね。
次は、
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L162
この辺。
app
-> to_app
という流れでここに来ると、先程の@handlers
をreverse
してinject
してます。
その中でbuild
しているので、Hanlder
のbuild
を見ると、
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L47
klass
をnewして(引数にapp渡してる)返してますね。
つまり、最初useした時は
- url_encode
- json
という順番で追加しているので、これをreverseしてbuildすると
https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb#L150
結果、↑ここの@app
はurl_encodeのオブジェクトになる為、
- url_encode
- json
- 大本のapp
という順番で実行されるわけですね。
Middlewareを作ってみる
というわけで、とりあえず簡単なMiddlewareを作ってみたいと思います。
デフォルトのlogger
だとbodyを出力してくれないので、bodyを出力するmiddlewareを書いてみます。
require "faraday" require "json" require 'logger' class BodyDumper def initialize app @app = app @logger = Logger.new(STDOUT) end def call env dump_body("request", env) @app.call(env).on_complete do dump_body("response", env) end end private def dump_body phase, env @logger.info("#{phase} : #{env.body}") if env.body end end client = Faraday.new(:url => "http://localhost:4567") do |faraday| faraday.request :url_encoded faraday.use BodyDumper faraday.adapter Faraday.default_adapter end res = client.post "/test", { hoge: 100, foo: :bar }
まとめ
FaradayはMiddlewareなどでの拡張性(柔軟性)がうりな気がします。
(どっかのWebAPIを呼ぶクライアントを作ったりとか向いてるかも)
adapterも結構用意されていて、em_http
もあるみたいですね。
あとはfaraday_middleware
にoatuh2
のmiddlewareがあって、
Authorization
ヘッダー埋め込んでくれたりとか、地味に便利でした。
もう少し踏み込んで、拡張して使ってみたいと思います。
次回は気が向いたら、parameterのescape周りを修正してみたい、、、かも。
デフォルトだと半角スペースが+
にescapeされるので。