rest-clientの使い方 まとめ
- 感想とか
- リクエスト
- RestClient.get: GET
- RestClient.post: POST
- RestClient.delete: DELETE
- RestClient.patch: PATCH
- RestClient.put: PUT
- RestClient.head: HEAD
- RestClient.options: OPTIONS
- RestClient::Request.execute: HTTPメソッド指定
- 使い方
- 必須の引数
- オプションの引数
- :headers: リクエストヘッダ
- :cookies: クッキー
- :user: ベーシック認証のuser
- :password: ベーシック認証のpassword
- :block_response: レスポンス時の処理
- :raw_response: Responseの代わりにRawResponseを使う
- :max_redirects: リダイレクト回数の上限値
- :proxy: プロキシのURI
- :verify_ssl: SSLのverifyの設定
- timeout: open_timeoutとread_timeoutを両方同時にセット
- open_timeout: コネクションを開くまでに待つ最大秒数
- read_timeout: データ読み込みまでに待つ最大秒数
- :before_execution_proc: リクエスト前のフック(proc)
- :ssl_client_cert: クライアント証明書
- :ssl_client_key: クライアント証明書の秘密鍵
- :ssl_ca_file: CA証明書ファイル
- :ssl_ca_path,: CA証明書ファイルを含むディレクトリ
- :ssl_cert_store: CA証明書を含む証明書ストア
- :ssl_verify_callback: 検証をフィルタするコールバック
- :ssl_verify_callback_warnings: trueなら警告
- :ssl_version: SSLバージョン
- :ssl_ciphers: 利用可能な共通鍵暗号の種類
- GETとPOSTでパラメータのとり方が違う
- リクエストはステータスコードによって挙動が異なる
- レスポンス
- RESTfulなリソース
- クエリパラメータ
- タイムアウト
- リダイレクト
- ファイル転送(ストリーミング)
- プロキシ
- クッキー
- SSL/TLS
- ログ
- レスポンスのコールバック
- リクエスト失敗例外
- フック
- restclientコマンド
- 落ち葉拾い
感想とか
注意点
- これは自分用のまとめを公開したものです。ドキュメント/ソースコードを見ただけで試していないコードも多いので、参考程度に。
感想
- ActiveResourceみたいな感じかと思ったら全然違った。普通のHTTPクライアントでRESTっぽさはあんまりない。
- Faradayやhttp.rbのがシンプルで使いやすそう。まぁでも有名なgemならどれでも十分良い気がする。
- 日本語情報は少ないので、情報探すならreadme -> ソースコードが良さそう。どちらも情報量多くて読みやすい。
pros
- ドキュメントがしっかりしてる
- RestClient::Resourceでリソースを表現できるのが便利そう
cons
- 日本語情報は少なめ。海外では人気だけど日本ではあんまり人気ない?
- 他のHTTPクライアントに比べると、APIがちょい複雑?getとpostでパラメータの取り方が変わったりするの、若干ややこしい気がする
リクエスト
RestClient.get: GET
# API RestClient.get(url, headers={}) # 基本 RestClient.get 'http://example.com/resource' # クエリパラメータ # クエリパラメータはheadersのparamsで指定する。ややこしい。 -> 将来変更の可能性あり RestClient.get 'http://example.com/resource', {params: {id: 50, 'foo' => 'bar'}} # リクエストヘッダ RestClient.get 'https://user:[email protected]/private/resource', {accept: :json}
RestClient.post: POST
- urlencodedされる
# API RestClient.post(url, payload, headers={}) # 基本 RestClient.post 'http://example.com/resource', {param1: 'one', nested: {param2: 'two'}} # raw payloads RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain' RestClient.post 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf' # json RestClient.post "http://example.com/resource", {'x' => 1}.to_json, {content_type: :json, accept: :json} # Multipart(ファイルあり) RestClient.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb') # Multipart(ファイルなし) RestClient.post '/data', {:foo => 'bar', :multipart => true}
RestClient.delete: DELETE
RestClient.delete 'http://example.com/resource'
RestClient.patch: PATCH
RestClient.put: PUT
RestClient.head: HEAD
RestClient.head('http://example.com').headers
RestClient.options: OPTIONS
RestClient.options('http://example.com')
RestClient::Request.execute: HTTPメソッド指定
使い方
- .getとかではproxy等のオプションは利用できないっぽいので、そういう場合のみ.executeを使う
- .getメソッド等は内部で.executeを利用している
# RestClient.get('http://example.com/resource') と同じ RestClient::Request.execute(method: :get, url: 'http://example.com/resource') # オプション指定1 RestClient::Request.execute(method: :get, url: 'http://example.com/resource', timeout: 10) # オプション指定2 RestClient::Request.execute(method: :get, url: 'http://example.com/resource', ssl_ca_file: 'myca.pem', ssl_ciphers: 'AESGCM:!aNULL') # オプション指定3 RestClient::Request.execute(method: :delete, url: 'http://example.com/resource', payload: 'foo', headers: {myheader: 'bar'}) # GET http://example.com/resource?foo=bar # クエリパラメータはheadersのparamsに指定する RestClient::Request.execute(method: :get, url: 'http://example.com/resource', timeout: 10, headers: {params: {foo: 'bar'}})
必須の引数
:method: HTTPメソッド
:url: URL
オプションの引数
:headers: リクエストヘッダ
:cookies: クッキー
:user: ベーシック認証のuser
:password: ベーシック認証のpassword
:block_response: レスポンス時の処理
:raw_response: Responseの代わりにRawResponseを使う
:max_redirects: リダイレクト回数の上限値
- デフォルト: 10
:proxy: プロキシのURI
- RestClient.proxyよりもこっちが優先される
:verify_ssl: SSLのverifyの設定
- デフォルト: OpenSSL::SSL::VERIFY_PEER
timeout: open_timeoutとread_timeoutを両方同時にセット
- タイムアウト 参照
open_timeout: コネクションを開くまでに待つ最大秒数
read_timeout: データ読み込みまでに待つ最大秒数
:before_execution_proc: リクエスト前のフック(proc)
- RestClient.add_before_execution_proc相当
:ssl_client_cert: クライアント証明書
Net::HTTP#cert=
に対応
:ssl_client_key: クライアント証明書の秘密鍵
Net::HTTP#key=
に対応
:ssl_ca_file: CA証明書ファイル
Net::HTTP#ca_file
に対応
:ssl_ca_path,: CA証明書ファイルを含むディレクトリ
Net::HTTP#ca_path
に対応
:ssl_cert_store: CA証明書を含む証明書ストア
-
Net::HTTP#cert_store
に対応 -
ca_file/ca_pathより細かく設定したい場合はコレを使う
:ssl_verify_callback: 検証をフィルタするコールバック
Net::HTTP#verify_callback
に対応
:ssl_verify_callback_warnings: trueなら警告
:ssl_version: SSLバージョン
Net::HTTP#ssl_version=
に対応
:ssl_ciphers: 利用可能な共通鍵暗号の種類
Net::HTTP#ciphers=
に対応
GETとPOSTでパラメータのとり方が違う
- POST/PUT/PATCHはpayloadを取る。GET/DELETE/HEAD/OPTIONSはpayloadを取らない
- パラメータの指定方法も異なるので注意する
# API RestClient.get(url, headers={}) RestClient.post(url, payload, headers={}) # パラメータ指定 RestClient.get 'http://example.com', {params: {foo: "bar"}} # GET http://example.com?foo=bar RestClient.post 'http://example.com'', {}, {params: {foo: "bar"}} # POST http://example.com foo=bar RestClient.post 'http://example.com', {foo: "bar"} # POST http://example.com foo=bar(payload)
リクエストはステータスコードによって挙動が異なる
200系: responseオブジェクトを返す
300系: 自動でリダイレクト
400系/500系: 例外を投げる
- rescueして個別に処理する。
レスポンス
response.code: ステータスコード
response = RestClient.get 'http://example.com/resource' # => <RestClient::Response 200 "<!doctype h..."> response.code # => 200
response.cookies: クッキー
response.cookies # => {"Foo"=>"BAR", "QUUX"=>"QUUUUX"}
response.headers: レスポンスヘッダ
response.headers # => {:content_type=>"image/jpg; charset=utf-8", :cache_control=>"private" ... } response.headers[:content_type] # => 'image/jpg'
resopnse.raw_headers: レスポンスヘッダ(raw)
response.body: レスポンスボディ
response.body # => "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n ..."
response.to_s: レスポンスボディ(alias)
response.request: リクエストオブジェクト
- RestClient::Requestオブジェクト
response.request.url # => "http://httpbin.org/get"
response.cookie_jar: ?
response.history: リダイレクト時のレスポンスの配列
RESTfulなリソース
- RestClient::ResourceはRESTfulなリソースを表現できる
- ネストされたリソースなどに繰り返しアクセスしたい場合に便利
GET
resource = RestClient::Resource.new('http://some/resource') resource.get
ネストされたリソース
# それぞれがResourceオブジェクトで、getメソッド等を使える site = RestClient::Resource.new('http://example.com') # http://example.com posts = site['posts'] # http://example.com/posts first_post = posts['1'] # http://example.com/posts/1 comments = first_post['comments'] # http://example.com/posts/1/comments site.get # GET http://example.com posts.get # GET http://example.com/posts first_post.get # GET http://example.com/posts/1 comments.get # GET http://example.com/posts/1/comments
第二引数はオプション
- RestClient::Request.executeのオプションと同じっぽい
# API RestClient::Resource.new(url, options={}, backwards_compatibility=nil, &block) # リクエストヘッダ resource = RestClient::Resource.new('http://some/resource', :headers => { :client_version => 1 }) # タイムアウト resource = RestClient::Resource.new('http://slow', :read_timeout => 10) resource = RestClient::Resource.new('http://behindfirewall', :open_timeout => 10) # BASIC認証 resource = RestClient::Resource.new('http://protected/resource', {:user => 'user', :password => 'password'})
クエリパラメータ
GET
# GET "https://httpbin.org/get?foo=bar&baz=qux" # クエリパラメータはheadersのparamsで指定する。ややこしい。 -> 将来変更の可能性あり RestClient.get('https://httpbin.org/get', params: {foo: 'bar', baz: 'qux'})
GET(Railsスタイルの配列)
- デフォルトはRailsのスタイル(?)と同じ
# GET "https://http-params.herokuapp.com/get?foo[]=1&foo[]=2&foo[]=3" response = RestClient.get('https://http-params.herokuapp.com/get', params: {foo: [1,2,3]})
GET(フラットな配列)
- フラットな配列が欲しい場合は、RestClient::ParamsArrayを使う
# GET "https://httpbin.org/get?foo=1&foo=2" RestClient.get('https://httpbin.org/get', params: RestClient::ParamsArray.new([[:foo, 1], [:foo, 2]]))
GET(入れ子のハッシュ)
# GET "https://http-params.herokuapp.com/get?outer[foo]=123&outer[bar]=456" response = RestClient.get('https://http-params.herokuapp.com/get', params: {outer: {foo: 123, bar: 456}})
POST
# POST "https://httpbin.org/post", data: "foo=bar&baz=qux" RestClient.post('https://httpbin.org/post', {foo: 'bar', baz: 'qux'})
POST(JSON payload)
- rest-clientはJSONをそのままでは扱えないので、自分でJSON文字列にする必要がある
payload = {'name' => 'newrepo', 'description': 'A new repo'} RestClient.post('https://api.github.com/user/repos', payload.to_json, content_type: :json)
タイムアウト
timeout: open_timeoutとread_timeoutを両方同時にセット
- デフォルトは60s
- nilにするとタイムアウトしなくなる
- open_timeoutとread_timeoutを使うとより細かく指定できる
RestClient::Request.execute(method: :get, url: 'http://example.com/resource', timeout: 120)
open_timeout: コネクションを開くまでに待つ最大秒数
RestClient::Request.execute(method: :get, url: 'http://example.com/resource', read_timeout: 120, open_timeout: 240)
read_timeout: データ読み込みまでに待つ最大秒数
リダイレクト
基本的な使い方
# ステータスコードが300台の場合は、自動でリダイレクトしてくれる response = RestClient.get('http://httpbin.org/redirect/2') # response.historyで、リダイレクトによる一連のレスポンスにアクセスできる response.history # => [<RestClient::Response 302 "<!DOCTYPE H...">, <RestClient::Response 302 "">] # `max_redirects: 0`で自動リダイレクトしないようにできる RestClient::Request.execute(method: :get, url: 'http://httpbin.org/redirect/1', max_redirects: 0) # `RestClient::Found: 302 Found`例外が投げられる # 手動リダイレクト begin # 自動リダイレクトしないようにする RestClient::Request.execute(method: :get, url: 'http://httpbin.org/redirect/1', max_redirects: 0) rescue RestClient::ExceptionWithResponse => err # 例外オブジェクト err # => #<RestClient::Found: 302 Found> # 例外を通してresponseにアクセスできる err.response # => RestClient::Response 302 "<!DOCTYPE H..." err.response.headers[:location] # => "/get" # 手動リダイレクト err.response.follow_redirection # => RestClient::Response 200 "{\n "args":..." end
POSTのリダイレクト
- 自動リダイレクトはGET/HEADだけ。POSTとかは自分で.follow_redirectionを使って手動リダイレクトする
- いくつか方法がある。例外スタイルがおすすめらしい
例外スタイルの場合
- リダイレクト系の例外をrescueして、手動リダイレクト
begin RestClient.post('http://example.com/redirect', 'body') rescue RestClient::MovedPermanently, RestClient::Found, RestClient::TemporaryRedirect => err err.response.follow_redirection # 例外(301, 302, 307)の場合は、手動リダイレクト end
例外スタイル + ステータスコードで分岐の場合
- 例外をまるごとrescueして、ステータスコードで分岐からの、手動リダイレクト
begin RestClient.post('http://example.com/redirect', 'body') rescue RestClient::ExceptionWithResponse => err # 例外(301, 302, 307)の場合は、手動リダイレクト case err.http_code when 301, 302, 307 err.response.follow_redirection else raise end end
ブロックスタイルの場合
- ブロックでresponse時の処理を指定できる。そこでステータスコードで分岐からの、手動リダイレクト
RestClient.post('http://example.com/redirect', 'body') { |response, request, result| case response.code when 301, 302, 307 response.follow_redirection # 手動リダイレクト else response.return! # デフォルトの処理 end }
ファイル転送(ストリーミング)
リクエスト
# ファイルアップロード(text/plain) RestClient.put('http://httpbin.org/put', File.open('/tmp/foo.txt', 'r'), content_type: 'text/plain') # ファイルアップロード(multipart/form-data) RestClient.put('http://httpbin.org/put', {file_a: File.open('a.txt', 'r'), file_b: File.open('b.txt', 'r')})
レスポンス
- RestClient.getの時、レスポンスはメモリにバッファされる。しかしisoなどの大きいデータの場合は、メモリに収まらないのでファイルに直接ストリームしたい。 -> 2つの方法がある
raw_response: Tempfileに保存する
raw_response: true
にすると、レスポンスをTempfileに保存する- RestClient::Responseオブジェクトの代わりに、RestClient::RawResponseオブジェクトを返す
raw_response = RestClient::Request.execute( method: :get, url: 'http://releases.ubuntu.com/16.04.2/ubuntu-16.04.2-desktop-amd64.iso', raw_response: true ) # => <RestClient::RawResponse @code=200, @file=#<Tempfile:/tmp/rest-client.20170522-5346-1pptjm1>, @request=<RestClient::Request @method="get", @url="http://releases.ubuntu.com/16.04.2/ubuntu-16.04.2-desktop-amd64.iso">> raw_response.file.size # => 1554186240 raw_response.file.path # => "/tmp/rest-client.20170522-5346-1pptjm1"
block_response: procを使い自分でファイルに書き込む
File.open('/some/output/file', 'w') {|f| # proc # responseはNet::HTTPのNet::HTTPResponseオブジェクト。これを使い、自分でファイルに書き込む block = proc { |response| response.read_body do |chunk| f.write chunk end } # リクエスト RestClient::Request.execute(method: :get, url: 'http://example.com/some/really/big/file.img', block_response: block) }
プロキシ
グローバル
# プロキシサーバを利用する RestClient.proxy = "http://proxy.example.com/" RestClient.get "http://some/resource" # http_proxy環境変数を使う場合は、自分でしていしないといけないっぽい? RestClient.proxy = ENV['http_proxy']
リクエスト毎
# このリクエストだけ、プロキシサーバを利用する RestClient::Request.execute(method: :get, url: 'http://example.com', proxy: 'http://proxy.example.com') # このリクエストだけ、プロキシサーバを利用しない RestClient.proxy = "http://proxy.example.com/" RestClient::Request.execute(method: :get, url: 'http://example.com', proxy: nil)
クッキー
get
response = RestClient.get 'http://example.com/action_which_sets_session_id' response.cookies # => {"_applicatioN_session_id" => "1234"}
set
- 後方互換性のため、headersとして指定可能
- クッキーとして使えるオブジェクトは3種類
- Hash{String/Symbol => String}
- ArrayHTTP::Cookie
- HTTP::CookieJar(http-cookie gemの機能)
# hashとして指定 # Set-Cookie: foo=Value; Domain=.example.com; Path=/ # Set-Cookie: bar=123; Domain=.example.com; Path=/ RestClient::Request.new( url: 'http://example.com', method: :get, cookies: {:foo => 'Value', 'bar' => '123'} ) # HTTP::CookieJarとして指定 jar = HTTP::CookieJar.new jar.add HTTP::Cookie.new('foo', 'Value', domain: 'example.com', path: '/', for_domain: false) RestClient::Request.new(..., :cookies => jar) # postで利用(headersとして指定可能) response2 = RestClient.post( 'http://localhost:3000/', {:param1 => "foo"}, {:cookies => {:session_id => "1234"}} )
SSL/TLS
# HTTPSでは、自動でシステムのCA証明書を使う RestClient.get 'https://user:[email protected]/private/resource' # クライアント証明書を指定 RestClient::Resource.new( 'https://example.com', :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("cert.pem")), :ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"), :ssl_ca_file => "ca_certificate.pem", :verify_ssl => OpenSSL::SSL::VERIFY_PEER ).get # SSLのエラーが起きる場合はverifyを無視できる # もちろんセキュリティー的に問題ありなので本番で使わないこと RestClient.get 'https://user:[email protected]/private/resource', verify_ssl: false
ログ
グローバル
- logの指定方法は4つ
- loggerオブジェクト
- ファイル名
- 'stdout'
- 'stderr'
RestClient.log = 'stdout' # 環境変数でもOK ENV['RESTCLIENT_LOG'] = 'stdout'
リクエスト単位
# logオプションを使う resource = RestClient::Resource.new 'http://example.com/resource', log: Logger.new(STDOUT) RestClient::Request.execute(method: :get, url: 'http://example.com/foo', log: Logger.new(STDERR))
レスポンスのコールバック
基本
- ブロックを使うと、レスポンスのコールバックを指定できる
- ブロックの戻り値が、戻り値になる
RestClient.get('http://example.com/nonexistent') # 例外を投げる RestClient.get('http://example.com/nonexistent') {|response, request, result| response } # => <RestClient::Response 404 "<!doctype h...">
ステータスコードで処理を分岐
- ブロックではデフォルトの処理をしなくなるので、デフォルトの処理をしてほしい場合は
response.return!(&block)
を行う - ブロックを使う方法よりも、rescueを使う方法のほうが自然でおすすめらしい
# ブロックを使う方法 RestClient.get('http://example.com/resource') { |response, request, result, &block| case response.code when 200; response # 200の場合は、レスポンスをそのまま返す when 423; raise SomeCustomExceptionIfYouWant # 423の場合は、指定の例外 else ; response.return!(&block) # その他の場合は、デフォルトの処理 end } # rescueを使う方法(こっちのが自然でおすすめらしい) begin # リクエスト response = RestClient.get('http://example.com/resource') rescue RestClient::Unauthorized, RestClient::Forbidden => err # エラー1の場合 puts 'Access denied' return err.response rescue RestClient::ImATeapot => err # エラー2の場合 puts 'The server is a teapot! # RFC 2324' return err.response else # 成功の場合 puts 'It worked!' return response end
リクエスト失敗例外
リクエスト失敗例外の継承階層
- リクエスト時はこれらの例外をrescueする
- ステータスコード単位なら、RestClient::NotFoundなどをrescueする。まとめてrescueしたいならRestClient::RequestFailedをrescueする
RuntimeError: Rubyの例外 RestClient::Exception: RestClientのベースとなる例外 RestClient::ExceptionWithResponse: リクエスト失敗時の例外(互換性のため存在) RestClient::RequestFailed: リクエスト失敗時の例外 RestClient::NotFound等: ステータスコードに対応した例外(404なら、RestClient::NotFound)
リクエスト失敗例外をキャッチする
# キャッチしない RestClient.get 'http://example.com/nonexistent' # => RestClient::NotFoundが投げられる # キャッチする begin RestClient.get 'http://example.com/nonexistent' rescue RestClient::NotFound => exception exception.response # => <RestClient::Response 404 "<!doctype h..."> end
exception.response: レスポンス
exception.http_code: ステータスコード
exception.http_headers: レスポンスヘッダ
exception.http_body: レスポンスボディ
フック
- RestClient.add_before_execution_procを使うと、executionメソッドの実行前フックを仕掛けることができる
- :before_execution_procオプションで、リクエスト単位での実行も可能っぽい
RestClient.add_before_execution_proc do |request, params| # 任意の処理 end # リクエスト前に任意の処理が実行される RestClient.get 'http://example.com'
restclientコマンド
- gem installすると、シェルで使えるrestclientというコマンドがついてくる。
- そんなに便利ではなさそう。使わないかなー。
rest-clientをrequireした状態で、irbを起動
$ restclient >> RestClient.get 'http://example.com'
リソース指定
$ restclient http://example.com >> put '/resource', 'data'
認証
$ restclient https://example.com user pass >> delete '/private/resource'
設定ファイルを使う
1.設定ファイルを作成
# ~/.restclient sinatra: url: http://localhost:4567 rack: url: http://localhost:9292 private_site: url: http://example.com username: user password: pass
2.設定ファイルを読み込み
# $ restclient https://example.com user pass と同じ $ restclient private_site
単発のリクエスト
$ restclient get http://example.com/resource > output_body $ restclient put http://example.com/resource < input_body
落ち葉拾い
requireの方法は2つ
# 推奨 require 'rest-client' # 非推奨(後方互換性のため残ってる) require 'rest_client'
その他
- APIはSinatraのDSLに影響を受けた
- Ruby 2.0+
- v2.0でAPIが結構変わったので注意