[英]How to DRY up Rails 3 controllers by overriding methods like respond_with?
I'm trying to create a JSONP API for my Rails 3 application. 我正在尝试为我的Rails 3应用程序创建一个JSONP API。 Right now in my controllers, I have a lot of actions which follow this pattern:
现在在我的控制器中,我有很多遵循这种模式的动作:
# This is from my users_controller.rb, as an example
def index
@users = User.all
respond_with(@users, :callback => params[:callback])
end
While this works as is, I would like to DRY it up by not having to repeat the :callback => params[:callback]
in every action's call to respond_with
. 虽然这是按原样工作的,但我想在每次操作调用
respond_with
不必重复:callback => params[:callback]
来respond_with
。 How can I do this? 我怎样才能做到这一点?
Update: One thing I've realized that is ugly about my above code is that the :callback => params[:callback]
option will be passed for any response format, not just JSON. 更新:有一件事我已经意识到我上面的代码是丑陋的是
:callback => params[:callback]
选项将被传递给任何响应格式,而不仅仅是JSON。 The following code is probably more correct: 以下代码可能更正确:
def index
@users = User.all
respond_with(@users) do |format|
format.json { render :json => @users, :callback => params[:callback]}
end
end
There are a couple ways I've considered to address this problem, but I can't figure out how to make them work: 我有几种方法可以解决这个问题,但我无法弄清楚如何使它们工作:
render
(perhaps in the application controller) so that it accepts a :jsonp
option that automatically includes the :callback => params[:callback]
parameter. render
(可能在应用程序控制器中),以便它接受:jsonp
选项,该选项自动包含:callback => params[:callback]
参数。 This way I could change the above code to the following, which is somewhat shorter: def index
@users = User.all
respond_with(@users) do |format|
format.json { render :jsonp => @users}
end
end
to_json
in order to solve my problem. to_json
的响应器以解决我的问题。 That way I could leave out the block and just call respond_with(@users, :responder => 'MyResponder')
to solve the issue. respond_with(@users, :responder => 'MyResponder')
块,只需调用respond_with(@users, :responder => 'MyResponder')
来解决问题。 Or perhaps I could include this code in an application responder using plataformatec's responders gem so that respond_with(@users)
by itself would be sufficient. respond_with(@users)
本身就足够了。 Note that technically, it is incorrect to render JSON with a callback parameter, since you get a JavaScript response (a function call to the JSON-P callback) rather than a JSON result. 请注意,从技术上讲,使用回调参数呈现JSON是不正确的,因为您获得了JavaScript响应(对JSON-P回调的函数调用)而不是JSON结果。 So if you have
所以,如果你有
render :json => my_object, :callback => params[:callback]
and a request for /users?callback=func
comes in, Rails would answer 并且对
/users?callback=func
的请求进来了,Rails会回答
func({…})
with content type application/json
, which is incorrect, since the above response is clearly not JSON but JavaScript. 内容类型为
application/json
,这是不正确的,因为上面的响应显然不是JSON而是JavaScript。
The solution I use is 我使用的解决方案是
def respond_with_json(item)
respond_with do |format|
format.json { render :json => item }
format.js { render :json => item, :callback => params[:callback] }
end
end
which responds correctly with or without callback. 无论有没有回调,它都能正确响应。 Applying this to the aforementioned solution, we get:
将此应用于上述解决方案,我们得到:
def custom_respond_with(*resources, &block)
options = resources.extract_options!
if params[:callback]
old_block = block
block = lambda do |format|
old_block.call(format) if block_given?
format.js { render :json => resources[0], :callback => params[:callback] }
end
end
respond_with(*(resources << options), &block)
end
Also note the correction to resources[0]
, otherwise you end up wrapping resources
in an extra array as a result of the splat operator. 还要注意对
resources[0]
的更正,否则最终会因为splat运算符而将resources
包装在额外的数组中。
THere's a gem that can do this to: rack-jsonp-middleware . 这是一个可以做到这一点的宝石: rack-jsonp-middleware 。
The setup instructions are pretty scant on the site, but I did create a little Rails project that uses it - which you can take a look at the commits and see what I did to get the middleware up and running. 网站上的设置说明很少,但我确实创建了一个使用它的小Rails项目 - 您可以查看提交,看看我做了什么来启动和运行中间件。
https://github.com/rwilcox/rack_jsonp_example https://github.com/rwilcox/rack_jsonp_example
This is bit 'low-tech' compared to the reponder solution, but what about just creating a private method in your appliation_controller.rb to handle this. 与响应者解决方案相比,这有点“低技术”,但是只需在appliation_controller.rb中创建一个私有方法来处理这个问题。 The params variable will be available to it and you could pass the @users object to it.
params变量将可用,您可以将@users对象传递给它。
#application_controller.rb
private
def jsonp(my_object)
render :json => my_object, :callback => params[:callback]
end
#controller
def index
@users = User.all
respond_with(@users) do |format|
format.json { jsonp(@users)}
end
end
Thanks to samuelkadolph for helping me in the #rubyonrails IRC channel today. 感谢samuelkadolph今天在#rubyonrails IRC频道帮助我。 He provided a solution in this gist , copied below for convenience:
他提供了这个要点的解决方案,为方便起见,在下面复制:
def custom_respond_with(*resources, &block)
options = resources.extract_options!
if options[:callback]
old_block = block
block = lambda do |format|
old_block.call(format) if block_given?
format.json { render :json => [] }
end
end
respond_with(*(resources << options), &block)
end
I haven't tried this in my application yet, but I can see that it should work. 我还没有在我的应用程序中尝试过这个,但我可以看到它应该可行。 He also confirmed that I could similarly override the
respond_with
method itself simply by changing the name of this method and changing the last line of the definition to super(*(resources << options), &block)
. 他还确认我可以简单地通过更改此方法的名称并将定义的最后一行更改为
super(*(resources << options), &block)
来覆盖respond_with
方法本身。
I think this will work for me. 我认为这对我有用。 However, I'm still interested in knowing how to write a custom responder to do the job.
但是,我仍然有兴趣知道如何编写自定义响应程序来完成这项工作。 (It would be a more elegant solution, IMHO.)
(这将是一个更优雅的解决方案,恕我直言。)
Update: I tried this in my application and it works with some minor changes. 更新:我在我的应用程序中尝试了这个,它可以进行一些小的更改。 Here is the version I'm using now in the
private
section of my ApplicationController, designed to automatically provide the :callback => params[:callback]
option to JSON requests: 这是我现在在ApplicationController的
private
部分中使用的版本,旨在自动为JSON请求提供:callback => params[:callback]
选项:
def custom_respond_with(*resources, &block)
options = resources.extract_options!
if params[:callback]
old_block = block
block = lambda do |format|
old_block.call(format) if block_given?
format.json { render :json => resources, :callback => params[:callback] }
end
end
respond_with(*(resources << options), &block)
end
Note that I had to change if options[:callback]
to if params[:callback]
in order to get it working. 请注意,我必须将
if options[:callback]
更改为if params[:callback]
以使其正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.