3

我想在输出 JSON 时从 ActiveRecord/ActiveModel 类中过滤掉特定字段。

最直接的方法就是覆盖as_json,也许像这样:

def as_json (options = nil)
  options ||= {}
  super(options.deep_merge({:except => filter_attributes}))
end

def filter_attributes
  [:password_digest, :some_attribute]
end

这行得通,但它有点冗长,并且不适合干得很快。我认为用一个神奇的类方法声明过滤的属性会很好。例如:

class User < ActiveRecord::Base
  include FilterJson

  has_secure_password
  filter_json :password_digest
  #...
end

module FilterJson
  extend ActiveSupport::Concern

  module ClassMethods
    def filter_json (*attributes)
      (@filter_attributes ||= Set.new).merge(attributes.map(&:to_s))
    end

    def filter_attributes
      @filter_attributes
    end
  end

  def as_json (options = nil)
    options ||= {}
    super(options.deep_merge({:except => self.class.filter_attributes.to_a}))
  end
end

这样做的问题是让它正确处理继承。假设我是用户的子类:

class SecretiveUser < User
  filter_json :some_attribute, :another_attribute
  #...
end

:some_attribute从逻辑上讲,过滤掉,:another_attribute和 also是有意义的:password_digest

但是,这只会过滤类上声明的属性。为了达到预期的目的,我尝试在 inside 调用superfilter_attributes但失败了。我想出了这个,这是一个黑客。

def filter_attributes
  if superclass.respond_to?(:filter_attributes)
    superclass.filter_attributes + @filter_attributes
  else
    @filter_attributes
  end
end

这显然很脆弱且不习惯,但这是我想要完成的“什么”。谁能想到一种更正确(并希望更优雅)的方法?谢谢!

4

1 回答 1

4

我认为将属性列入白名单比将它们列入黑名单更安全。这将防止将来不需要的属性添加到您的 JSON 响应中或将其添加到您的 JSON 响应中,因为您忘记将所述属性添加到.UserSomeUserfilter_json

您似乎正在寻找特定继承问题的解决方案。我仍然要指出active_model_serializers,因为我觉得这是管理序列化的更明智的方式。

class UserSerializer < ActiveModel::Serializer
  attributes :id, :first_name, :last_name
end

class SecretUserSerializer < UserSerializer
  attributes :secret_attribute, :another_attribute
end

给定一些SecretUser s你可以做的

SecretUserSerializer.new(s).as_json

你会得到:id, :first_name, :last_name, :secret_attribute, 和:another_attribute. 继承按预期工作。

于 2013-09-04T01:37:42.950 回答