Ruby 从Sinatra内部呼叫Sinatra
我有一个基于Sinatra的REST服务应用程序,我想从一个路由中调用其中一个资源,有效地将一个资源与另一个资源组合起来。例如Ruby 从Sinatra内部呼叫Sinatra,ruby,sinatra,Ruby,Sinatra,我有一个基于Sinatra的REST服务应用程序,我想从一个路由中调用其中一个资源,有效地将一个资源与另一个资源组合起来。例如 get '/someresource' do otherresource = get '/otherresource' # do something with otherresource, return a new resource end get '/otherresource' do # etc. end 重定向将不起作用,因为我需要对第二个资源进行
get '/someresource' do
otherresource = get '/otherresource'
# do something with otherresource, return a new resource
end
get '/otherresource' do
# etc.
end
重定向将不起作用,因为我需要对第二个资源进行一些处理并从中创建一个新的资源。显然,我可以a)使用RestClient或其他客户机框架,或者b)构造我的代码,使otherresource的所有逻辑都在一个方法中,只需调用该方法,如果我可以使用Sinatra的DSL重新使用Sinatra内部的资源,感觉会更干净。我可以通过快速而肮脏的机架请求并直接调用Sinatra(机架应用程序)应用程序来破解一些东西。虽然不漂亮,但它很管用。请注意,最好将生成此资源的代码提取到helper方法中,而不是执行类似的操作。但这是可能的,而且可能有比这更好、更干净的方法
#!/usr/bin/env ruby
require 'rubygems'
require 'stringio'
require 'sinatra'
get '/someresource' do
resource = self.call(
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/otherresource',
'rack.input' => StringIO.new
)[2].join('')
resource.upcase
end
get '/otherresource' do
"test"
end
如果你想知道更多关于幕后发生的事情,我已经写了一些关于Rack基础知识的文章,你可以阅读。有和。这可能适用于您的情况,也可能不适用于您的情况,但当我需要创建这样的路线时,我通常会尝试以下方法:
%w(main other).each do |uri|
get "/#{uri}" do
@res = "hello"
@res.upcase! if uri == "other"
@res
end
end
另一种选择(我知道这并没有回答您的实际问题)是将您的公共代码(甚至模板渲染)放在助手方法中,例如:
helpers do
def common_code( layout = true )
@title = 'common'
erb :common, :layout => layout
end
end
get '/foo' do
@subtitle = 'foo'
common_code
end
get '/bar' do
@subtitle = 'bar'
common_code
end
get '/baz' do
@subtitle = 'baz'
@common_snippet = common_code( false )
erb :large_page_with_common_snippet_injected
end
在此基础上,我需要支持在lib/public
中获取静态文件以及查询参数和cookie(用于维护经过身份验证的会话)。我还选择在非200响应上引发异常(并在调用函数中处理它们)
如果您在Sinatra/base.rb
中跟踪Sinatra的self.call
方法,它会获取一个env
参数并使用它构建一个函数,因此您可以深入其中查看支持哪些参数
我不记得return语句的所有条件(我认为Ruby2做了一些更改),所以可以根据您的需求进行调整
下面是我正在使用的函数:
def get_route url
fn = File.join(File.dirname(__FILE__), 'public'+url)
return File.read(fn) if (File.exist?fn)
base_url, query = url.split('?')
begin
result = self.call('REQUEST_METHOD' => 'GET',
'PATH_INFO' => base_url,
'QUERY_STRING' => query,
'rack.input' => StringIO.new,
'HTTP_COOKIE' => @env['HTTP_COOKIE'] # Pass auth credentials
)
rescue Exception=>e
puts "Exception when fetching self route: #{url}"
raise e
end
raise "Error when fetching self route: #{url}" unless result[0]==200 # status
return File.read(result[2].path) if result[2].is_a? Rack::File
return result[2].join('') rescue result[2].to_json
end
Sinatra的文档涵盖了这一点-基本上,您使用底层
机架接口的调用方法:
触发另一条路线
有时候,传球并不是你想要的
您希望获得调用其他路线的结果。简单使用
呼吁实现这一目标:
谢谢,比起使用RestClient,我更喜欢这个。使用这种技术的包装器方法,如local_get、local_post、local_put和local_delete,将是对Sinatra框架的一个很好的补充。这个答案不会保留环境,包括用户的会话。这里有一个类似的答案(但看起来更糟糕的黑客攻击)来保护环境:Ruby 2.1.0,sinatra 1.4.4,我得到了#
的未定义的方法'join',啊,当结果是一个由/public/
提供的文件时,显然会发生这种情况,解决方法是:返回文件。如果结果[2],则读取(结果[2]。路径)。是不是?Rack::File
不是我想要的,但这是一种有用的技术。谢谢
get '/foo' do
status, headers, body = call env.merge("PATH_INFO" => '/bar')
[status, headers, body.map(&:upcase)]
end
get '/bar' do
"bar"
end