从 Sinatra 内部呼叫 Sinatra

发布于 2024-09-16 07:39:12 字数 426 浏览 3 评论 0原文

我有一个基于 Sinatra 的 REST 服务应用程序,我想从一条路线中调用其中一个资源,从而有效地从另一个资源组合一个资源。例如,

get '/someresource' do
  otherresource = get '/otherresource'
  # do something with otherresource, return a new resource
end

get '/otherresource' do
  # etc.
end

重定向将不起作用,因为我需要对第二个资源进行一些处理并从中创建一个新资源。显然,我可以 a) 使用 RestClient 或其他一些客户端框架,或者 b) 构建我的代码,以便其他资源的所有逻辑都在一个方法中,然后调用它,但是,如果我可以重新调用它,感觉会更干净通过 Sinatra 的 DSL 使用我的资源。

I have a Sinatra based REST service app and I would like to call one of the resources from within one of the routes, effectively composing one resource from another. E.g.

get '/someresource' do
  otherresource = get '/otherresource'
  # do something with otherresource, return a new resource
end

get '/otherresource' do
  # etc.
end

A redirect will not work since I need to do some processing on the second resource and create a new one from it. Obviously I could a) use RestClient or some other client framework or b) structure my code so all of the logic for otherresource is in a method and just call that, however, it feels like it would be much cleaner if I could just re-use my resources from within Sinatra using their DSL.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

甜点 2024-09-23 07:39:12

另一种选择(我知道这并没有回答您的实际问题)是将通用代码(甚至模板渲染)放在辅助方法中,例如:

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

Another option (I know this isn't answering your actual question) is to put your common code (even the template render) within a helper method, for example:

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
酷遇一生 2024-09-23 07:39:12

Sinatra 的文档涵盖了这一点 - 本质上您使用底层 rack 接口的 call 方法:

http://www.sinatrarb.com/intro.html#Triggering%20Another%20Route

触发另一条路由

有时候pass并不是你想要的,而是
您想获得调用另一条路线的结果。只需使用
调用以实现此目的:

get '/foo' do
  status, headers, body = call env.merge("PATH_INFO" => '/bar')
  [status, headers, body.map(&:upcase)]
end

get '/bar' do
  "bar"
end

Sinatra's documentation covers this - essentially you use the underlying rack interface's call method:

http://www.sinatrarb.com/intro.html#Triggering%20Another%20Route

Triggering Another Route

Sometimes pass is not what you want, instead
you would like to get the result of calling another route. Simply use
call to achieve this:

get '/foo' do
  status, headers, body = call env.merge("PATH_INFO" => '/bar')
  [status, headers, body.map(&:upcase)]
end

get '/bar' do
  "bar"
end
没企图 2024-09-23 07:39:12

我能够通过发出快速而肮脏的机架请求并直接调用 Sinatra(机架应用程序)应用程序来破解一些东西。它不漂亮,但很有效。请注意,最好将生成此资源的代码提取到辅助方法中,而不是执行类似的操作。但这是可能的,而且可能有比这更好、更干净的方法。

#!/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 基础知识的文章,您可以阅读。有 什么是 Rack?使用机架

I was able to hack something up by making a quick and dirty rack request and calling the Sinatra (a rack app) application directly. It's not pretty, but it works. Note that it would probably be better to extract the code that generates this resource into a helper method instead of doing something like this. But it is possible, and there might be better, cleaner ways of doing it than this.

#!/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

If you want to know more about what's going on behind the scenes, I've written a few articles on the basics of Rack you can read. There is What is Rack? and Using Rack.

弄潮 2024-09-23 07:39:12

这可能适用也可能不适用于您的情况,但是当我需要创建这样的路线时,我通常会尝试以下操作:

%w(main other).each do |uri|
  get "/#{uri}" do
    @res = "hello"
    @res.upcase! if uri == "other"
    @res
  end
end

This may or may not apply in your case, but when I’ve needed to create routes like this, I usually try something along these lines:

%w(main other).each do |uri|
  get "/#{uri}" do
    @res = "hello"
    @res.upcase! if uri == "other"
    @res
  end
end
迷荒 2024-09-23 07:39:12

基于 AboutRuby 的回答,我需要支持在 lib/public 中获取静态文件以及查询参数和 cookie(用于维护经过身份验证的会话。)我还选择对非 200 响应引发异常(并在调用函数中处理它们)。

如果您在 sinatra/base.rb 中跟踪 Sinatra 的 self.call 方法,它会采用一个 env 参数并构建一个 Rack::Request ,这样您就可以深入了解支持哪些参数。

我不记得 return 语句的所有条件(我认为 Ruby 2 进行了一些更改),因此请随意调整您的要求。

这是我正在使用的函数:

  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

Building on AboutRuby's answer, I needed to support fetching static files in lib/public as well as query paramters and cookies (for maintaining authenticated sessions.) I also chose to raise exceptions on non-200 responses (and handle them in the calling functions).

If you trace Sinatra's self.call method in sinatra/base.rb, it takes an env parameter and builds a Rack::Request with it, so you can dig in there to see what parameters are supported.

I don't recall all the conditions of the return statements (I think there were some Ruby 2 changes), so feel free to tune to your requirements.

Here's the function I'm using:

  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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文