简体   繁体   English

如何通过覆盖respond_with等方法来干掉Rails 3控制器?

[英]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: 我有几种方法可以解决这个问题,但我无法弄清楚如何使它们工作:

  • Override 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
  • Create a responder that overrides 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. 或许我可以使用plataformatec的响应者gem将此代码包含在应用程序响应程序中,以便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]以使其正常工作。

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. 基本上你可以为你的控制器创建一个“默认”的respond_to,这样你就可以让你的所有动作默认响应json。

was that what you were asking? 是你问的那个?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM