Respond_with在 Rails 3 中使用 Devise & attr_accessible



我正在使用Rails3构建一个API,使用dev来处理一些身份验证。

我通常使用responsd_with方法为各种资源返回xml/json。

例如GET/groups.xml将路由到

def index
  respond_with Group.all
end

对于各种资源,这在我的网站上运行良好,并返回格式良好的json或xml,其中包含每个组的所有属性。

然而,当我调用GET/users.xml时,它只使用每个用户属性的有限子集进行响应。事实证明,这里只会返回attr_assessible中定义的属性——我怀疑这是设计的一个"特性",因为任何其他模型都不是这样。

有人能启发我吗?

编辑:这在Devise 1.4.2中有点固定。有关

的详细信息,请参见下文

您的怀疑是正确的。Devise Authenticatable模块覆盖#to_xml#to_json,以首先检查类是否响应#accessible_attributes方法,如果响应了,则输出仅限于#accessible _attributes返回的那些属性。authenticable.rb中的代码在这里:

  %w(to_xml to_json).each do |method|
    class_eval <<-RUBY, __FILE__, __LINE__
      def #{method}(options={})
        if self.class.respond_to?(:accessible_attributes)
          options = { :only => self.class.accessible_attributes.to_a }.merge(options || {})
          super(options)
        else
          super
        end
      end
    RUBY
  end

您会注意到,这段代码将#accessible_attributes的结果合并到任何传入的选项中。因此,您可以指定:only选项,例如:

.to_xml(:only => [:field, :field, :field])

这将覆盖Devise强加的限制,并生成仅包括您指定的字段的xml输出。你需要包括你想要暴露的每个字段,因为一旦你使用:只有你才能胜过正常操作。

在这种情况下,我认为您将无法继续在控制器中使用responsd_with快捷方式,因为您需要直接指定xml输出。您可能不得不回到一个老派块的响应:

respond_to do |format|
  format.xml { render :xml => @users.to_xml(:only => [:field, :field, :field]) }
  format.html
end

正如您已经发现的,您还可以在模型类中添加您想要通过attr_accessible公开的其他字段。然而,这会产生额外的副作用,使这些字段可以批量分配,在这种情况下,您可能不一定想要这样做。

早期版本(<1.4.2)的Devise对to_json和to_xml方法执行了一次monkeypatch,用attr_accessible中定义的属性覆盖:only=>[]选项。烦人的

现在已经更改了这一点,因此serializable_hash将被覆盖,并且在to_json或to_xml中设置的任何:only=>[:attribute]选项都将被持久化。

在我的例子中,我最终自己对to_json进行了monkeypatch,并添加了一个可访问所有ActiveRecord模型的方法api_accessible。

class ActiveRecord::Base
  def to_json(options = {}, &block)
    if self.class.respond_to?(:api_attributes)
      super(build_serialize_options(options), &block)
    else
      super(options, &block)
    end
  end
  class << self
    attr_reader :api_attributes
    def api_accessible(*args)
      @api_attributes ||= []
      @api_attributes += args
    end
  end
  private
    def build_serialize_options(options)
      return options if self.class.api_attributes.blank?
      methods = self.class.instance_methods - self.class.attribute_names.map(&:to_sym)
      api_methods = self.class.api_attributes.select { |m| methods.include?(m) }
      api_attrs = self.class.api_attributes - api_methods
      options.merge!(only: api_attrs) if api_attrs.present?
      options.merge!(methods: api_methods) if api_methods.present?
      return options
    end
end

这意味着您现在可以定义一个属性列表(和方法!),这些属性将在调用to_json时默认公开。Respond_with还使用to_json,因此它对API很有效。

例如,user.rb

class User < ActiveRecord::Base
 devise :database_authenticatable, :registerable, :confirmable,
         :recoverable, :rememberable, :trackable, :validatable
  #Setup accessible (or protected) attributes for your model
  attr_accessible :email,
                  :password,
                  :password_confirmation,
                  :remember_me,
                  :first_name,
                  :last_name,

  api_accessible :id,
                 :name,
                 :created_at,
                 :first_name,
                 :last_name,
                 :some_model_method_also_works
end

相关内容

  • 没有找到相关文章

最新更新