2

我正在使用 Rails 3 构建一个 API,使用设计来处理一些身份验证。

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

例如 GET /groups.xml 将路由到

def index
  respond_with Group.all
end

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

但是,当我调用 GET /users.xml 时,它只响应每个用户属性的有限子集。事实证明,这里只会返回 attr_assessible 中定义的属性 - 我怀疑这是设计的“功能”,因为任何其他模型都不是这种情况。

任何人都可以启发我吗?

编辑:这在 Devise 1.4.2 中是固定的。详情见下文

4

2 回答 2

1

你的怀疑是正确的。Devise Authenticatable 模块覆盖#to_xml#to_json以首先检查类是否响应#accessible_attributes方法,如果响应,则输出仅限于#accessible_attributes 返回的那些属性。来自authenticate.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])

这将覆盖设计强加的限制并生成仅包含您指定的字段的 xml 输出。您将需要包含您想要公开的每个字段,因为一旦您使用 :only 您将胜过正常操作。

在这种情况下,我认为您不能继续在控制器中使用respond_with快捷方式,因为您需要直接指定 xml 输出。您可能不得不退回到老式的respond_to块:

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

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

于 2011-06-25T01:17:32.860 回答
1

Devise 的旧版本 (< 1.4.2) 对 to_json 和 to_xml 方法执行了一个猴子补丁,用 attr_accessible 中定义的属性覆盖 :only => [] 选项。恼人的。

现在已经改变了,因此 serializable_hash 被覆盖,并且在 to_json 或 to_xml 中设置的任何 :only => [:attribute] 选项都被保留。

就我而言,我最终自己对 to_json 进行了猴子修补,并向所有 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
于 2011-07-08T18:22:41.187 回答