7

我正在尝试为我的 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(可能在应用程序控制器中),以便它接受: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 的响应程序 gem将此代码包含在应用程序响应程序中,这样respond_with(@users)就足够了。
4

5 回答 5

8

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

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

并且请求/users?callback=func进来,Rails 会回答

func({…})

使用 content type 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],否则由于 splat 运算符,您最终会包装resources在一个额外的数组中。

于 2011-03-10T11:34:06.690 回答
3

有一个 gem 可以做到这一点:rack-jsonp-middleware

网站上的设置说明非常少,但我确实创建了一个使用它的小 Rails 项目 - 您可以查看提交并了解我为启动和运行中间件所做的工作。

https://github.com/rwilcox/rack_jsonp_example

于 2011-04-20T03:38:14.563 回答
1

与响应程序解决方案相比,这有点“低技术”,但是在您的 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
于 2010-12-17T16:54:00.187 回答
1

感谢 samuelkadolph 今天在#rubyonrails IRC 频道中帮助我。他在这个要点中提供了一个解决方案,为方便起见,复制如下:

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

我还没有在我的应用程序中尝试过这个,但我可以看到它应该可以工作。他还确认我可以类似地重写respond_with方法本身,只需更改此方法的名称并将定义的最后一行更改为super(*(resources << options), &block).

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

更新:我在我的应用程序中尝试了这个,它可以进行一些小的更改。这是我现在在private我的 ApplicationController 部分中使用的版本,旨在自动为: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]才能使其正常工作。

于 2010-12-17T22:44:55.443 回答
0

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

那是你问的吗?

于 2011-04-20T03:47:47.443 回答