2010-12-17 22 views
7

我想为我的Rails 3应用程序创建一个JSONP API。现在在我的控制器,我有很多的其遵循这种模式操作:如何通过覆盖像respond_with方法干掉Rails 3控制器?

# 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 (可能在应用程序控制器中),以便它接受:jsonp选项,该选项自动包含:callback => 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's responders gem将此代码包含在应用程序响应程序中,这样respond_with(@users)本身就足够了。

回答

1

感谢samuelkadolph今天帮助我#rubyonrails IRC频道。他提供了this gist的解决方案,复制下面为了方便:

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 

我还没有在我的应用程序尝试这样做,但我可以看到,它应该工作。他还确认,我可以简单地通过更改此方法的名称并将定义的最后一行更改为super(*(resources << options), &block)来同样覆盖respond_with方法本身。

我认为这对我很有用。但是,我仍然有兴趣知道如何编写自定义响应者来完成这项工作。 (这将是一个更优雅的解决方案,恕我直言。)

更新:我在我的应用程序中试过这个,它可以在一些细微的变化下工作。这是现在我用我的ApplicationController中的private部分版本,旨在自动提供:callback => params[:callback]选项JSON请求:得到它的工作

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 

注意,我必须为了改变if options[:callback]if params[:callback]

1

与reponder解决方案相比,这有点“低科技”,但是在appliation_controller.rb中创建一个私有方法来处理这个问题。 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 
8

请注意,从技术上讲,使用回调参数呈现JSON是不正确的,因为您获得JavaScript响应(对JSON-P回调的函数调用)而不是JSON结果。 所以如果你有

render :json => my_object, :callback => params[:callback] 

/users?callback=func请求到来时,Rails会回答

func({…}) 

与内容类型application/json,这是不正确的,因为上面的反应显然不是JSON,但JavaScript的。

我使用的解决方案是

def respond_with_json(item) 
    respond_with do |format| 
    format.json { render :json => item } 
    format.js { render :json => item, :callback => params[:callback] } 
    end 
end 

其具有或不具有回调正确响应。将此应用于上述方案中,我们得到:

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 

还要注意校正resources[0],否则你最终在一个额外的数组作为图示操作的结果包装resources

+0

谢谢,这是一个很好的观点! – evanrmurphy 2011-03-11 20:22:01

3

这是一个可以做到这一点的宝石:rack-jsonp-middleware

安装说明在网站上很少,但我确实创建了一个使用它的小Rails项目 - 您可以看一下提交并查看我做了什么来启动和运行中间件。

https://github.com/rwilcox/rack_jsonp_example

+1

这是一个很棒的宝石,几乎不需要任何整合。非常干净优雅的解决方案。不够感谢你。 – plainjimbo 2012-05-02 00:29:01

0

您也可以看看这个answer。基本上,你可以为你的控制器创建一个“default”respond_to,这样你就可以让你的所有动作默认为响应json。

是你在问什么?