如何通过重写像 respond_with 这样的方法来干燥 Rails 3 控制器?
我正在尝试为我的 Rails 3 应用程序创建 JSONP API。现在在我的控制器中,我有很多遵循此模式的操作:
# This is from my users_controller.rb, as an example
def index
@users = User.all
respond_with(@users, :callback => params[:callback])
end
虽然这按原样工作,但我想通过不必重复 :callback => 来干燥它。 params[:callback]
在每个操作的 respond_with
调用中。我该怎么做?
更新:我意识到上面的代码丑陋的一件事是 :callback => params[:callback]
选项将为任何响应格式传递,而不仅仅是 JSON。下面的代码可能更正确:
def index
@users = User.all
respond_with(@users) do |format|
format.json { render :json => @users, :callback => params[:callback]}
end
end
我考虑过几种方法来解决这个问题,但我不知道如何让它们工作:
- 覆盖
render
(也许在应用程序控制器中) ),以便它接受自动包含:callback => 的
参数。这样我可以将上面的代码更改为以下代码,该代码稍微短一些::jsonp
选项。 params[:callback]
def index
@users = User.all
respond_with(@users) do |format|
format.json { render :jsonp => @users}
end
end
- 创建一个覆盖
to_json
的响应程序以解决我的问题。这样我就可以省略该块,只需调用respond_with(@users, :responder => 'MyResponder')
来解决问题。或者,也许我可以使用 plataformatec 的响应程序 gem 将此代码包含在应用程序响应程序中,以便respond_with(@用户)
本身就足够了。
I'm trying to create a JSONP API for my Rails 3 application. 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
. 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. 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:
- Override
render
(perhaps in the application controller) so that it accepts a:jsonp
option that automatically includes the:callback => params[:callback]
parameter. 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
- Create a responder that overrides
to_json
in order to solve my problem. That way I could leave out the block and just callrespond_with(@users, :responder => 'MyResponder')
to solve the issue. Or perhaps I could include this code in an application responder using plataformatec's responders gem so thatrespond_with(@users)
by itself would be sufficient.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
请注意,从技术上讲,使用回调参数渲染 JSON 是不正确的,因为您得到的是 JavaScript 响应(对 JSON-P 回调的函数调用)而不是 JSON 结果。
因此,如果您收到
/users?callback=func
的请求,Rails 会使用内容类型
application/json
进行响应,这是不正确的,因为上面的响应是显然不是 JSON,而是 JavaScript。我使用的解决方案是
在有或没有回调的情况下都能正确响应。将其应用于上述解决方案,我们得到:
另请注意对
resources[0]
的更正,否则由于 splat,您最终会将resources
包装在额外的数组中操作员。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.
So if you have
and a request for
/users?callback=func
comes in, Rails would answerwith content type
application/json
, which is incorrect, since the above response is clearly not JSON but JavaScript.The solution I use is
which responds correctly with or without callback. Applying this to the aforementioned solution, we get:
Also note the correction to
resources[0]
, otherwise you end up wrappingresources
in an extra array as a result of the splat operator.有一个 gem 可以做到这一点:rack-jsonp-middleware。
网站上的设置说明非常少,但我确实创建了一个使用它的小 Rails 项目 - 您可以查看提交并了解我为启动和运行中间件所做的工作。
https://github.com/rwilcox/rack_jsonp_example
THere's a gem that can do this to: 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.
https://github.com/rwilcox/rack_jsonp_example
与响应器解决方案相比,这有点“低科技”,但是在您的 appliation_controller.rb 中创建一个私有方法来处理这个问题怎么样? params 变量将可供它使用,并且您可以将 @users 对象传递给它。
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. The params variable will be available to it and you could pass the @users object to it.
感谢 samuelkadolph 今天在 #rubyonrails IRC 频道中为我提供的帮助。他在 this gist 中提供了一个解决方案,为了方便起见,复制如下:
我还没有在我的应用程序中尝试过这个,但我可以看到它应该有效。他还确认,我可以通过更改此方法的名称并将定义的最后一行更改为
super(*(resources << options) 来类似地覆盖
。respond_with
方法本身,&块)我认为这对我有用。但是,我仍然有兴趣了解如何编写自定义响应程序来完成这项工作。 (恕我直言,这将是一个更优雅的解决方案。)
更新:我在我的应用程序中尝试了这个,它可以进行一些小的更改。这是我现在在 ApplicationController 的
private
部分中使用的版本,旨在自动提供:callback => JSON 请求的 params[:callback]
选项:请注意,我必须将
if options[:callback]
更改为if params[:callback]
才能让它发挥作用。Thanks to samuelkadolph for helping me in the #rubyonrails IRC channel today. He provided a solution in this gist, copied below for convenience:
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 tosuper(*(resources << options), &block)
.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:Note that I had to change
if options[:callback]
toif params[:callback]
in order to get it working.您还可以查看此答案。基本上,您可以为控制器创建一个“默认”respond_to,这样您就可以将所有操作默认为响应 json。
这就是你问的吗?
You can also check out this answer. basically you can create a "default" respond_to for your controller so you can just make your all your actions default to responding to json.
was that what you were asking?