【问题标题】:Calling Sinatra from within Sinatra从 Sinatra 内部调用 Sinatra
【发布时间】:2014-07-05 05:22:49
【问题描述】:

我有一个基于 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。

【问题讨论】:

    标签: ruby sinatra


    【解决方案1】:

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

    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
    

    【讨论】:

      【解决方案2】:

      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
      

      【讨论】:

        【解决方案3】:

        我能够通过快速而肮脏的机架请求并直接调用 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 基础知识的文章,您可以阅读。有What is Rack?Using Rack

        【讨论】:

        • 谢谢,我喜欢这个比使用 RestClient 更好。 local_get、local_post、local_put 和 local_delete 等包装方法使用这种技术将是对 Sinatra 框架的一个很好的补充。
        • 这个答案不保留环境,包括用户的会话。这是保护环境的类似答案(但看起来更糟糕的黑客攻击):stackoverflow.com/questions/5824736/…
        • Ruby 2.1.0,sinatra 1.4.4,我得到undefined method `join' for #<Rack::File:0x0000000242f750>
        • 啊,当结果是由/public/ 提供的文件时,显然会发生这种情况,解决方法是:return File.read(result[2].path) if result[2].is_a? Rack::File
        【解决方案4】:

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

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

        【讨论】:

        • 不是我想要的,但仍然是一种有用的技术。谢谢。
        【解决方案5】:

        AboutRuby's answer 的基础上,我需要支持在 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-02-07
          • 1970-01-01
          • 2014-02-10
          • 2012-11-09
          • 2013-03-13
          • 1970-01-01
          • 2015-06-23
          • 2012-12-10
          相关资源
          最近更新 更多