Rails中inclusion校验不生效:Role类可成功创建非合规名称角色
校验完全失效的原因与修复方案
核心触发原因
- Hash语法书写错误,导致Rails完全丢失了inclusion校验的必填规则参数
原代码里的校验行写为:
Ruby中Hash的符号键正确写法要求键名和冒号紧密相邻,也就是validates :name, inclusion: { in : rolesPrefix, message: "not a valid Role Prefix"}in: rolesPrefix(等价于老式写法:in => rolesPrefix)。当前写法在in和冒号之间插入了多余空格,Ruby解析时根本不会将in识别为Hash内的规则键,最终传递给Rails的inclusion校验参数中,完全缺少用于定义匹配范围的in选项。
按照Rails的校验机制,当某个内置校验缺少必填参数时,会静默跳过整个校验逻辑,不会抛出错误,也不会执行任何匹配判断,自然无法拦截非法的角色名。 - 即使修复语法错误,现有inclusion校验的逻辑也不符合业务预期
原代码的业务逻辑是「角色名只要以列表内的任意前缀开头即为合法」,但inclusion校验是完全等值匹配规则——要求字段值必须和列表内的某一个元素完全相等才算通过。
按照你提供的前缀列表值["super_", "AUG", "CHI", "INN", "NCH", "PAL"],现有规则只会允许name等于这些纯前缀字符串(比如值刚好是"super_"),反而会把业务上合法的角色名(比如"super_admin"、"AUG_staff")全部拦截;同时对完全不在列表内的值,因为校验本身没触发,自然可以直接写入数据库。 - 类加载时一次性固定前缀列表的实现存在隐患
原代码在类定义的阶段直接执行Institution.find(1).departments生成前缀列表,这个逻辑只会在应用启动加载类的时候运行一次,如果后续部门数据发生新增、修改,这个固定的前缀列表不会同步更新,会出现校验规则和实际业务数据脱节的问题;如果加载类时Institution id=1的记录不存在,还会直接抛出数据库查询错误,导致应用启动失败。
修复后的可运行代码
class Role < ApplicationRecord rolify role_cname: 'Permission' has_and_belongs_to_many :users, join_table: :users_roles # 动态生成合法前缀列表,替代类加载时的静态局部变量 def self.valid_role_prefixes default_prefixes = ['super_'] # 增加空值保护,避免部门数据不存在时报错 institution = Institution.find_by(id: 1) return default_prefixes unless institution&.departments default_prefixes + institution.departments.pluck(:department_short) end validates_uniqueness_of :name # 自定义校验实现前缀匹配逻辑 validate :name_must_match_valid_prefix belongs_to :resource, polymorphic: true, optional: true validates :resource_type, inclusion: { in: Rolify.resource_types }, allow_nil: true scopify private def name_must_match_valid_prefix # 检查角色名是否匹配任意合法前缀 unless self.class.valid_role_prefixes.any? { |prefix| name.to_s.start_with?(prefix) } errors.add(:name, "not a valid Role Prefix") end end end
修复效果
- 执行
Role.create(name: "should_not_be_eccepted")时,因为该名称不以任何合法前缀开头,会被校验拦截,不会写入数据库,同时在模型错误信息中返回对应的提示。 - 业务上合法的角色名(比如
"super_admin"、"AUG_staff")可以正常通过校验写入。
内容的提问来源于stack exchange,提问作者Geogre
相关产品推荐
相关产品推荐

