-
-
Save godfat/5676447 to your computer and use it in GitHub Desktop.
require 'rest-core' | |
module RestCore | |
middleware = Class.new | |
middleware.module_eval do | |
def initialize app | |
@app = app | |
end | |
def call env, &k | |
puts "Calling" | |
@app.call(env, &k) | |
end | |
end | |
Circular = Builder.client do | |
use middleware | |
end | |
Circular::Middleware = middleware | |
end | |
puts RC::Circular | |
puts RC::Circular::Middleware | |
puts RC::Circular.new.head('http://example.com/') |
require 'rest-core' | |
module RestCore | |
middleware = Class.new | |
middleware.module_eval do | |
def initialize app | |
@app = app | |
end | |
def call env, &k | |
puts env['secret'] | |
@app.call(env, &k) | |
end | |
end | |
Secret = Builder.client(:secret) do | |
use middleware | |
end | |
Secret::Middleware = middleware | |
end | |
puts RC::Secret.new(:secret => 'mission').head('http://example.com/') |
require 'rest-core/middleware' | |
require 'rest-core/util/hmac' | |
class RestCore::Signature | |
def self.members; [:secret]; end | |
include RestCore::Middleware | |
def call env, &k | |
uri = request_uri(env) | |
sig = Hmac.sha1(secret(env), uri) | |
app.call(env.merge(REQUEST_QUERY => {}, | |
REQUEST_PATH => "#{uri}&Signature=#{sig}", &k)) | |
end | |
end |
可能是因為 DefaultSite
?
DefaultSite
的行為是調整 REQUEST_PATH
, 經過或還沒經過 DefaultSite
會影響 REQUEST_PATH
https://github.com/cardinalblue/rest-core/blob/master/lib/rest-core/middleware/default_site.rb#L8-L16
def call env, &k
path = if env[REQUEST_PATH].to_s.start_with?('http')
env[REQUEST_PATH]
else
"#{site(env)}#{env[REQUEST_PATH]}"
end
app.call(env.merge(REQUEST_PATH => path), &k)
end
同時 request_uri
會把 REQUEST_PATH
跟 REQUEST_QUERY
串起來:
https://github.com/cardinalblue/rest-core/blob/master/lib/rest-core/middleware.rb#L55-L64
def request_uri env
# compacting the hash
if (query = (env[REQUEST_QUERY] || {}).select{ |k, v| v }).empty?
env[REQUEST_PATH].to_s
else
q = if env[REQUEST_PATH] =~ /\?/ then '&' else '?' end
"#{env[REQUEST_PATH]}#{q}#{percent_encode(query)}"
end
end
public :request_uri
了解,因為SHA1得套在query param,我來改改看用 percent_encode 然後手動 append 到後面試試看。
應該是可以直接用 request_uri, 最後再 append 上去即可,除非他要的不是直接的 uri 而是別的。不過你上次給我看的版本是直接用,所以照 signature.rb 那個應該是可以?
規格是所有 query parameter 集合起來取 SHA1(也就是 "a=1&b=2" 這字串作 SHA1)
我好像做好了,不過有個地方會有問題(secret 只抓到 nil)
喔喔,我忘記 query 不包含 path...
你碰到的 nil 正是我昨天修的 bug.
其實 Builder.client(:api_key)
那邊不用給 :secret,
因為他會自動去 middleware 裡面撈出來用
這樣給反而是重複了。新版中,修正這個問題,
就算重複也不致於產生 nil
嗨抱歉週末沒有看。
剛剛我試了一下,好像還是 nil。
喔還有 irb 在下這段指令過後,
過一段時間�就會突然冒出 ruby-2.0.0-p0/lib/ruby/2.0.0/irb/input-method.rb:152:in `readline': execution expired (Timeout::Error)
不知道你有遇過嗎?
看了一下,發現因為少打一個 *
, 在 Client.new 那邊誤傳了 array 進去
def self.new *args, &block
Client.new *args, &block
# ^^
end
因此 api_key
跟 secret
沒設進去。改成這樣可動:
c = RestCore::YahooBuy.new
c.api_key = 'api_key'
c.secret = 'secret'
我自己是都用 rib, 目前沒碰過那個狀況
我試試看能不能在 irb 下重現?看起來不應該有這個錯
你在什麼情況下會碰到?試了一下試不出來
啊謝謝,我碰到的情況是忘了用 * 號時,c.get('getCurrTime')
報錯後,console我就有五秒鐘可以作任何事,之後就突然冒出這訊息結束 console。
2.0.0-p0 :003 > c.get('getCurrTime')TypeError: no implicit conversion of nil into String
from /Code/rest-more/rest-core/lib/rest-core/util/hmac.rb:17:in `digest'
...
from /.rvm/rubies/ruby-2.0.0-p0/bin/irb:16:in `<main>'
2.0.0-p0 :004 > a = 1
=> 1
2.0.0-p0 :005 > b = 2
=> 2
2.0.0-p0 :006 > c = 3
=> 3
2.0.0-p0 :007 > /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/input-method.rb:152:in `readline': execution expired (Timeout::Error)
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/input-method.rb:152:in `gets'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:472:in `block (2 levels) in eval_input'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:624:in `signal_status'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:471:in `block in eval_input'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:190:in `call'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:190:in `buf_input'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:105:in `getc'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/slex.rb:206:in `match_io'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/slex.rb:76:in `match'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:290:in `token'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:266:in `lex'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:237:in `block (2 levels) in each_top_level_statement'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:233:in `loop'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:233:in `block in each_top_level_statement'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:232:in `catch'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:232:in `each_top_level_statement'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:488:in `eval_input'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:397:in `block in start'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:396:in `catch'
from /.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:396:in `start'
from /.rvm/rubies/ruby-2.0.0-p0/bin/irb:16:in `<main>'
我知道是什麼原因了 O_o
這跟 irb 或 rib 無關,應該也算是 bug 吧 @@"
是我的 timeout 實作造成的
仔細想想這樣做是不太好...
我想想看要怎麼修比較好
嗯辛苦了XD
想問一下概念問題,可以看看我的理解有沒有錯誤嗎:
middleware 的 members 有兩個功能,首先是用來在 Client Class 設立時預備 middleware 層級的變數用(如預設 base url)。不過他也有第二個功能,是開個介面能去抓 env 裡面的變數,所以像是 secret 這類 instance 層級的東西還是需要設立 members才有方便的介面跟env取值。
Builder.client() 裡面要丟進去的 symbols 那部份我讀不太懂。是不是代表宣告 client instance variable呢?要產生新的 client instance 時用 hash 丟這些參數進去就會存到特殊的地方而不是當作 query param,如 RC::Facebook 的 app_id?
這也讓我我不懂為何 Yahoo的 secret 不能放在 client builder 裡面,這是 client instance 層級的變數,在 Client.new 時傳入(跟 api_key 是一對的)。
謝謝解惑~
啊然後想問,要是得把 api_key放在每個 request 中,這是那個 default_payload 還是 default_query 的範圍嗎?
對,一方面給一些方便的 methods 可用,另一方面則是讓 client 也可以記得一份可改變的設定(變數)
對,Builder.client
給的參數是額外需要記得的變數,確實如 RC::Facebook
的 app_id
, 不過這裡並沒有例外,這個 app_id
也同樣會傳到 env 裡面,只是 middleware 裡面可能不會用到而已。
意思就是,在之前的版本裡(現已修好),如果 Builder.client
和 middleware 都給 secret
, 則 client 實際上會有兩個 secret
(!) 修正好的版本在所有的 attributes 中取 uniq
, 因此 secret 會只剩一個。
這樣並不會造成任何問題(在新的版本裡),只是沒有意義而已。也可以寫成:
class Signature
def initialize app; @app = app; end
def call env, &k
puts env['secret']
app.call(&k)
end
end
Client = RC::Builder.client(:secret){ use Signature }
也就是說,middleware 裡面就不自己記得一份 secret 了,一律靠 client 傳過去。這樣做的缺點是,使用 middleware 的人要自己記得這個 middleware 可能需要一份 secret 傳進去。
至於 api_key 如果要放在每個 request 中,那取決於要放在 query 或是 payload 甚或 headers 裡,
我會用對應的 middleware 並定義 default_query. 範例:
Client = RC::Builder.client(:api_key){ use RC::DefaultQuery }
class Client
def default_query
{:api_key => api_key}
end
end
這樣 client 可記得 api_key, 同時會自動夾在 query 裡面。payload 或 headers 亦然。
請問一下,你說的 dry run是原本就會丟出 timeout 囉?
2.0.0-p0 :002 > client = RC::Facebook.new
=> #<struct RestCore::Facebook timeout=10, site="https://gra..., headers={"Accept"=>"..., access_token=nil, log_method=nil, cache=nil, expires_in=600, error_handler=#<Proc:0x007..., error_detector=#<Proc:0x007..., json_response=true, defaults={:old_site=>..., data={}, app_id=nil, secret=nil, old_site="https://api...>
2.0.0-p0 :003 > client.request_full({RC::REQUEST_PATH => '/'}, client.dry)
=> {"REQUEST_METHOD"=>:get, "REQUEST_PATH"=>"https://graph.facebook.com//", "REQUEST_QUERY"=>{}, "REQUEST_PAYLOAD"=>{}, "REQUEST_HEADERS"=>{"Accept"=>"application/json", "Accept-Language"=>"en-us"}, "core.fail"=>[], "core.log"=>[#<struct RestCore::Event::Requested duration=0.010871, message="GET https://graph.facebook.com//">], "timeout"=>10, "site"=>"https://graph.facebook.com/", "headers"=>{"Accept"=>"application/json", "Accept-Language"=>"en-us"}, "access_token"=>nil, "log_method"=>nil, "cache"=>nil, "expires_in"=>600, "error_handler"=>#<Proc:0x007fd739686420@Code/rest-more/lib/rest-core/client/facebook.rb:18 (lambda)>, "error_detector"=>#<Proc:0x007fd7396864c0@Code/rest-more/lib/rest-core/client/facebook.rb:19 (lambda)>, "json_response"=>true, "defaults"=>{:old_site=>"https://api.facebook.com/"}, "data"=>{}, "app_id"=>nil, "secret"=>nil, "old_site"=>"https://api.facebook.com/", "async.callback"=>false, "async.timer"=>#<RestCore::Timeout::TimerThread:0x007fd7396a6dd8 @timeout=10, @error=#<Timeout::Error: execution expired>, @block=#<Proc:0x007fd7396a6d88@Code/rest-more/rest-core/lib/rest-core/middleware/timeout/timer_thread.rb:9 (lambda)>, @canceled=false, @thread=#<Thread:0x007fd7396a6d60 sleep>>, "RESPONSE_BODY"=>nil}
2.0.0-p0 :004 > .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/input-method.rb:152:in `readline': execution expired (Timeout::Error)
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/input-method.rb:152:in `gets'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:472:in `block (2 levels) in eval_input'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:624:in `signal_status'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:471:in `block in eval_input'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:190:in `call'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:190:in `buf_input'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:105:in `getc'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/slex.rb:206:in `match_io'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/slex.rb:76:in `match'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:290:in `token'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:266:in `lex'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:237:in `block (2 levels) in each_top_level_statement'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:233:in `loop'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:233:in `block in each_top_level_statement'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:232:in `catch'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb/ruby-lex.rb:232:in `each_top_level_statement'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:488:in `eval_input'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:397:in `block in start'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:396:in `catch'
from .rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/irb.rb:396:in `start'
from .rvm/rubies/ruby-2.0.0-p0/bin/irb:16:in `<main>'
有點不了解 request_uri ,自己 debug 時有看到回傳只有 query param,也有看到整個完整 url 的情形。是為什麼呢?