【问题标题】:How to DRY up Rails 3 controllers by overriding methods like respond_with?如何通过覆盖诸如 respond_with 之类的方法来干掉 Rails 3 控制器?
【发布时间】:2011-05-26 22:27:01
【问题描述】:

我正在尝试为我的 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

虽然这是按原样工作的,但我希望不必在每个操作对 respond_with 的调用中重复 :callback => params[:callback] 来干掉它。我该怎么做?

更新:我意识到我上面的代码很难看的一件事是: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 => params[:callback] 参数的:jsonp 选项。这样我就可以把上面的代码改成下面这样,稍微短一些:
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) 本身就足够了。

【问题讨论】:

    标签: ruby-on-rails jsonp dry ruby-on-rails-3 respond-with


    【解决方案1】:

    请注意,从技术上讲,使用回调参数呈现 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 作为splat 运算符的结果包装在一个额外的数组中。

    【讨论】:

      【解决方案2】:

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

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

      https://github.com/rwilcox/rack_jsonp_example

      【讨论】:

      • 这是一个很棒的宝石,几乎不需要任何集成。非常干净优雅的解决方案。感激不尽。
      【解决方案3】:

      与响应程序解决方案相比,这有点“低技术”,但是在您的 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
      

      【讨论】:

        【解决方案4】:

        感谢 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
        

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

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

        更新:我在我的应用程序中尝试了这个,它可以通过一些小的改动来工作。这是我现在在 ApplicationController 的 private 部分使用的版本,旨在自动为 JSON 请求提供 :callback =&gt; 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
        

        请注意,我必须将 if options[:callback] 更改为 if params[:callback] 才能使其正常工作。

        【讨论】:

          【解决方案5】:

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

          你问的是这个吗?

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-02-16
            • 1970-01-01
            • 2011-11-12
            • 2014-03-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多