我对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,但是当我刷新页面时,代码不会重新运行。
也许我误解了控制器的用途?我是不是想把这段代码放到其他地方?或者是否存在缓存问题?
答案 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.foo
或ReportsController.foo
- 当场解雇他们。
如果它是一个非常简单的一次性,你可以在控制器中将它放在私有方法中。
一些地方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中提取,则需要考虑以下几点:
- 理想情况下,呼叫需要RESTful(除非您使用多线程)
- 需要在实例方法
中进行调用 醇>
您当前的模式调用类上的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:
这将向您展示如何在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