我正在使用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