Rails版本:'〜> 4.2.7.1'
狂欢版:'3.1.1'
TlDr:
如何在Rails 4应用程序的中间件中获取路由/api/products/:id
或该路由的控制器和操作。
我在rails应用程序中添加了一个类似于gem scout_statsd_rack的中间件。这会将以下middleware添加到rails应用以通过statsd发送指标:
def call(env)
(status, headers, body), response_time = call_with_timing(env)
statsd.timing("#{env['REQUEST_PATH']}.response", response_time)
statsd.increment("#{env['REQUEST_PATH']}.response_codes.#{status.to_s.gsub(/\d{2}$/,'xx')}")
# Rack response
[status, headers, body]
rescue Exception => exception
statsd.increment("#{env['REQUEST_PATH']}.response_codes.5xx")
raise
end
def call_with_timing(env)
start = Time.now
result = @app.call(env)
[result, ((Time.now - start) * 1000).round]
end
我想要的是在中间件中找到当前路由,以便我可以发送特定于每条路由的指标。
我尝试了here描述的方法,它告诉env['PATH_INFO']
可以提供路径,但是它提供了这样的URL参数:/api/products/4
但我想要的是{{1}我的目的是跟踪/api/products/:id
API的效果。
/api/products/:id
和env['REQUEST_PATH']
也会给出相同的回复。
env['REQUEST_URI']
或者像这样
Rails.application.routes.router.recognize({"path_info" => env['PATH_INFO']})
但它出现了以下错误:
NoMethodError(未定义的方法
Rails.application.routes.router.recognize(env['PATH_INFO'])
find_routes'
vendor / bundle / gems / actionpack-4.2.7.1 / lib / action_dispatch / journey / router.rb:59:inpath_info' for {"path_info"=>"/api/v1/products/4"}:Hash):
call'
vendor/bundle/gems/actionpack-4.2.7.1/lib/action_dispatch/journey/router.rb:100:in
此answer讨论了recognize'
,但我如何访问变量
vendor/bundle/gems/scout_statsd_rack-0.1.7/lib/scout_statsd_rack.rb:27:in
request.original_url
,我认为它应该与request
相同但不能从此获取路由
您可以看到示例仓库here,其中包含rails中间件here的代码,可以按README中所述完成此设置,并且可以点击此API:{{ 1}}。
我尝试了@MichałMłoźniak给出的方法如下:
env
但我收到了以下回复:
http://localhost:3000/api/v1/products/1
我也推动了更改here。
答案 0 :(得分:9)
您需要将ActionDispatch::Request
或Rack::Request
传递给recognize
方法。以下是另一个应用程序的示例:
main:0> req = Rack::Request.new("PATH_INFO" => "/customers/10", "REQUEST_METHOD" => "GET")
main:0> Rails.application.routes.router.recognize(req) { |route, params| puts params.inspect }; nil
{:controller=>"customers", :action=>"show", :id=>"10"}
=> nil
同样适用于ActionDispatch::Request
。在中间件中,您可以轻松创建此对象:
request = ActionDispatch::Request.new(env)
如果您需要有关已识别路线的更多信息,可以通过recognize
方法查看要阻止的路线对象。
<强>更新强>
上述解决方案适用于普通的Rails路由,但由于您只安装了狂欢引擎,因此需要使用不同的类
request = ActionDispatch::Request.new(env)
Spree::Core::Engine.routes.router.recognize(request) { |route, params|
puts params.inspect
}
我想最好的方法是找到适用于普通路由和引擎的任意组合的通用解决方案,但这适用于您的情况。
更新#2
对于更一般的解决方案,您需要查看Rails路由器的来源,您可以在ActionDispatch
模块中找到它。查看Routing
和Journey
模块。我发现如果这是一个调度员,可以测试从recognize
方法返回的路由。
request = ActionDispatch::Request.new(env)
Rails.application.routes.router.recognize(req) do |route, params|
if route.dispatcher?
# if this is a dispatcher, params should have everything you need
puts params
else
# you need to go deeper
# route.app.app will be Spree::Core::Engine
route.app.app.routes.router.recognize(request) do |route, params|
puts params.inspect
}
end
end
这种方法适用于您的应用,但不一般。例如,如果您安装了sidekiq,route.app.app
将为Sidekiq::Web
,因此需要以不同方式处理。基本上要有一般解决方案,您需要处理Rails路由器支持的所有可安装引擎。
我想最好能够构建能够涵盖当前应用程序中所有案例的内容。所以要记住的是,当识别出初始请求时,屈服于黑色的route
的值可以是调度员。如果是,你有正常的Rails路由,如果不是,你需要递归检查。