Rails:我在哪里放置我的API方法?

时间:2016-02-03 23:43:36

标签: ruby-on-rails ruby model-view-controller

我对Rails很新,而且我做一些像创建API调用之类的简单事情,我有点不知所措。我已在/reports设置了一条有此控制器的路线:

class ReportsController < ApplicationController

  @client = # Api-accessing gem

  @all_reports = []

  def self.request_report

    begin
      puts "Step 1:"
      step1 = @client.request_report(opts = {"max_count" => 1})
      step1_result = step1.parse
      puts "Done!"
      puts step1_result

    rescue Excon::Errors::ServiceUnavailable => e
      puts "Didn't work"
      logger.warn e.response.message
      retry
    end
  end # End request_report

  request_report

end

当我第一次加载/reports路由时,这会正确调用外部API,但是当我刷新页面时,代码不会重新运行。

也许我误解了控制器的用途?我是不是想把这段代码放到其他地方?或者是否存在缓存问题?

3 个答案:

答案 0 :(得分:7)

控制器的唯一公共API是响应HTTP请求的 actions 。在您的情况下,get "/reports" => "reports#request_report"是与行为request_report对应的路线。

但是,操作是实例方法,而不是类方法:

class ReportsController
  def request_report # self.request_report would make this a class method!
    # @todo get reports from somewhere and
    # return some sort of response.
  end

  # any method call here happens when the class is evaluated.
end

您将该操作声明为类方法,然后在评估ReportsController类时调用它。很抱歉,但关于控制器的一切都是错误的。

Rails惯例是调用操作index

Rails中的控制器只能由路由器(或您的测试框架)实例化。因此,他们肯定是放置可用的钻头和凸起的错误位置。如果您看到某人正在ReportsController.new.fooReportsController.foo - 当场解雇他们。

那么你在哪里放置外部API调用?

如果它是一个非常简单的一次性,你可以在控制器中将它放在私有方法中。

一些地方API调用模型层 - 但是这是有争议的,因为ActiveRecord模型已经被赋予权力和责任的鳃增压。

一个对我有用的解决方案是Service Objects。它们易于测试,并且有明确的单一责任。

class RequestReportService
  def initalize(client)
    @client = client
  end 
  def call(opts = {})
   begin
      return @client.request_report(opts.merge("max_count" => 1))
    rescue Excon::Errors::ServiceUnavailable => e
      nil
    end
  end
end

class ReportsController
  def index
    @reports = RequestReportService.new(@client).call
  end
end

答案 1 :(得分:2)

要添加@max出色的答案,您需要了解Rails基于无状态协议(HTTP)......

  

每个请求消息都可以[仅]在隔离中理解。

这意味着如果你想创建一组控制器动作,你必须要知道每次调用都会创建一个新的instance类等等。这与{{{{ 3}}一组动作,应该为你提供构建你的功能的基础。

-

#config/routes
scope constraints: { subdomain: "api" } do
  resources :reports #-> http://api.url.com/reports
end

#app/controllers/reports_controller.rb
class ReportsController < ApplicationController
   respond_to :json #-> requires "responders" gem

   def index #-> instance method
      @reports = Report.all
      respond_with @reports #-> all reports
   end

   def show
      @report = Report.find params[:id]
      respond_with @report
   end
end

我会留下服务对象的东西,因为我没有经验。

-

如果您从外部API中提取,则需要考虑以下几点:

  
      
  1. 理想情况下,呼叫需要RESTful(除非您使用多线程)
  2.   
  3. 需要在实例方法
  4. 中进行调用   

您当前的模式调用上的API,这就是您无法刷新它的原因:

class ReportsController < ApplicationController
  @client = # Api-accessing gem

@client仅在类中被调用(我不知道它为什么有效,因为它应该是asynchronous)。

因此,如果您发送新的request(创建ReportsController实例),则@client将被声明为一次。

要使其正常工作,需要使用每个实例方法定义@client

class ReportsController < ApplicationController
  def index
    @client = # Api-accessing gem

这样,每次调用ReportsController#index时,都会进行新的API调用。可能看似微不足道,但数据范围很大。

最后,您需要阅读class variable

MVC (Model View Controller)

这将向您展示如何在Rails应用程序等中使用controllers

答案 2 :(得分:1)

好吧,我实际上从未在rails控制器中看到过这样的代码。 Rails是一个mvp框架。控制器用于在模型和视图之间进行协商。首先,如果您正确地路由到您的控制器,如 get "/reports" => "request_report#reports" 你的控制器应该有如下方法 def request_report @client = Client.find(params[:id]) end 然后控制器将在app / views / reports / request_report.html.erb中呈现并显示视图,并且可以访问刚从数据库中搜索的@client变量。

我不确定你为什么要在页面底部调用块request_report,它在控制器中没有意义。而且你当然不需要在控制器方法面前写自己。 def self.request_report your code end 至于把你的api控制器放在哪里,通常用于api控制器,我们可以在控制器下创建新的文件夹,所以结构就像 app/controllers/api/v1/your_api_controller.rb 然后在your_api_controller.rb中,您将需要像这样添加控制器的命名空间。 class Api::V1::ReportsController < ActionController::Base end 它与您的路由相同,您将在route.rb中添加命名空间 namespace :api do namespace :v1 do get "/reports" => "request_report#reports" end end