通常验证逻辑应该放在模型中,而不是控制器......但我有一种情况,我得到一个JSON POST请求,它给出了一个项目编号列表,其中每个数字应该是一个PK到DB表中。
我想检查每个PK是否存在,但不想使用ActiveRecord {{1}进行 n SELECT
查询,只需使用SELECT ... IN (x,y,...)
进行1次查询},.where(id: idsThatShouldBeInDb)
是一个整数数组。
我已经尝试了以下非常低劣的小测试以检查安全性
idsThatShouldBeInDb
这会在服务器的控制台窗口中生成:
Item.where(id: [1,2,3,'a', nil, '']).count
我所希望的是,非整数参数会抛出验证异常,因此模型基本上验证了输入数据。
在我看来,当我接收数据作为JSON字符串并使用SELECT COUNT(*)
FROM "items"
WHERE (("items"."id" IN (1, 2, 3, 0, 0) OR "trip_items"."id" IS NULL))
将其解析为哈希时,我必须检查
JSON.parse
并在上面创建不安全且不正确的nil
IS NULL
。因此,正在进行验证的控制器(而不是模型)(或至少是其中的一部分)。我应该指出我并不懒惰......很高兴在控制器中验证代码,我只是认为这确实倾向于将验证任务分布在两个模块中而不是很好地包含在模型中...
从Ruby,或者甚至是MVC的角度来看,验证收到的JSON数据的最佳方法是什么?我会去模型中的所有验证,但似乎控制器,在这种情况下必须做一些自己的验证。有没有更好的办法?谢谢!
答案 0 :(得分:1)
我会这样做:
[1] pry(main)> [1,2,3,'a', nil, ''].map(&:to_int)
NoMethodError: undefined method `to_int' for "a":String
from (pry):1:in `map'
如果在非Integer对象上调用它, to_int
会引发异常,所以如果你想验证pks是否是整数而没有命中DB,这是你最好的选择,因此你离开模型时其数据库相关活动
答案 1 :(得分:0)
如果其中一个键不在主键中(除了nil),Item.find([1,2,3,'a',nil,''])将引发异常。假设您信任数据库主键,我会做类似......
的操作begin
raise if !pks.all?(&:present?)
Item.find pks
rescue
end
答案 2 :(得分:0)
可以肯定的是,您似乎要验证传递的id列表是否为合法整数(不是0,负数,字符或nil
)。一个简单的:
myList.any?{|i| i.to_i <= 0}
应该足以检测到这一点(尽管如果这是一个真正的问题,它不会考虑浮点数)。您可以将其添加为辅助模块方法(扩展ActiveSupport :: Concern),并将此模块包含在您需要进行此检测的任何模型或控制器中。
除此之外,我不认为这是验证原始用户输入的清理。在我看来,这种关注在控制器中非常受欢迎(尽管如上所述,模块可能更合适)。这与模型验证相比,模型验证检查模型的行为,状态和持久性是我们期望的。
此外,我不确定您要通过此次清理检查确切地完成了什么。看起来消费者会知道id应该是正整数,因此任何丢失的记录都不应该是意料之外的。将0或NULL传递给主键上的搜索不会产生任何结果,并且似乎不会向我打开任何安全漏洞。但也许我很天真...你能准确地澄清你的担忧吗?或者只是想让用户意识到他们形成不良的查询?