如何在类Facebook应用中避免ActiveRecord的UniqueViolation错误?
解决ActiveRecord::RecordNotUnique错误及权限验证问题
嗨,我来帮你搞定这两个头疼的问题:重复点击移除按钮触发的数据库唯一约束错误,还有权限验证逻辑失效导致的越权操作。
1. 先搞定重复创建隔离记录的错误
你现在的quarantine方法用了create!,这货不管三七二十一就往数据库插新记录,但你的表已经加了user_id + micropost_id的唯一索引,重复插肯定撞墙。
换成find_or_create_by!就完美解决了——它会先查有没有已经存在的隔离记录,有就直接返回,没有才创建新的,完全不会触发重复插入错误:
def quarantine current_user.micropost_quarantines.find_or_create_by!(micropost_id: @micropost.id) # 这里加个友好提示,用户体验更好 redirect_to request.referer || root_url, notice: "这条动态已经从你的信息流里移除啦" end
2. 修复权限验证的逻辑漏洞
你之前改的entitled_user逻辑有问题,unless里的|| @quarantine.nil?相当于说“只要已经隔离过,不管用户有没有权限都能继续操作”,这直接把你原本的权限限制给绕过去了,难怪能隔离非关注用户的动态。
正确的逻辑应该是:先验证用户有没有权限(要么是动态的发布者,要么关注了发布者),没权限直接踢走;有权限的话,再检查是不是已经隔离过,是的话就提示用户别重复操作:
def entitled_user @micropost = Micropost.find(params[:id]) # 第一步:检查用户是否有权限操作这条动态 has_permission = current_user == @micropost.user || current_user.following.exists?(@micropost.user.id) unless has_permission redirect_to root_url, alert: "你没权限操作这条动态哦" return end # 第二步:检查是不是已经隔离过了,避免重复操作 if current_user.micropost_quarantines.exists?(micropost_id: @micropost.id) redirect_to request.referer || root_url, notice: "这条动态早就从你信息流里移除啦" end end
这里顺便提个小优化:current_user.following.include?(@micropost.user)会把所有关注的用户都加载到内存里再判断,用户关注的人多了会很慢,换成current_user.following.exists?(@micropost.user.id)会直接生成SQL查询,效率高很多。
最后说一句
你给micropost_quarantines表加的唯一索引非常棒,这是数据库层面的最后一道防线,配合我们在应用层的判断,双重保障不会出现重复记录。
内容的提问来源于stack exchange,提问作者Asarluhi
相关产品推荐
相关产品推荐

