为什么ActiveModel::Attributes的attribute方法会导致控制器许可的强参数丢失?
问题成因
分两种常见场景对应不同根因:
- 若确认
foo_params返回结果中bazes为nil(强参数阶段就被过滤)
绝大多数情况是项目中使用了基于模型属性自动生成强参数许可列表的逻辑(比如直接写permit(Foo.attribute_names)这类简写):
- 用
attribute :bar, :string声明属性时,Foo.attribute_names返回值仅包含attribute方法显式声明的bar,bazes不在许可列表中,会被强参数过滤返回nil - 替换为
attr_accessor :bar时,ActiveModel::Attributes的属性列表逻辑未触发,自动强参数逻辑会 fallback 到读取其他可写属性列表,bazes会被纳入许可范围
- 若
foo_params中能拿到bazes,但赋值给Foo实例后bazes为nil
这是ActiveModel::Attributes的默认行为:引入该模块后,批量赋值(Foo.new(foo_params)/foo.assign_attributes(foo_params))只会处理attribute方法显式声明的属性,哪怕单独定义了attr_reader :bazes/attr_accessor :bazes,只要没有用attribute声明bazes,赋值时就会直接忽略该参数。
解决方案
对应场景可选择以下方案:
- 若需要保留
ActiveModel::Attributes的类型转换能力,直接在模型中显式声明bazes属性即可:
class Foo include ActiveModel::Model include ActiveModel::Attributes attribute :bar, :string # 声明数组类型的bazes,默认值设为空数组避免nil问题 attribute :bazes, :array, default: [] end
不需要额外保留attr_reader :bazes,attribute方法会自动生成对应的读写方法。
- 若使用了自动强参数逻辑且不想修改原有逻辑,可手动扩展模型的属性列表:
class Foo include ActiveModel::Model include ActiveModel::Attributes attribute :bar, :string attr_reader :bazes def self.attribute_names super + %w[bazes] end end
- 若不需要类型转换能力,直接使用
attr_accessor声明所有属性即可。 - 额外检查Rswag配置:确认接口schema定义中是否正确声明了
bazes字段、类型是否设为array,未在schema中声明的参数会被Rswag测试默认过滤。
内容的提问来源于stack exchange,提问作者Benjamin Oakes
相关产品推荐
相关产品推荐

