绕过before_filter但仅在从不同的控制器/视图访问时

时间:2011-10-26 06:58:34

标签: ruby-on-rails

我在某些操作上运行了一个before_filter来检查用户是否是current_user。

before_filter :correct_user,  :only => [:edit, :update, :destroy]

def update
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
    redirect_to current_user, :notice => "User updated!"
  else
    redirect_to current_user, :notice => "User not updated. waa waa."
  end
end

private
def correct_user
  if current_user != @user        
    redirect_to root_url, :notice => "Cannot act on different user."
  end
end

不确定这是否是最好的做事方式,但它有效(也许更好的做法是简单地使用current_user而不是通过params找到@user?)

现在用户有很多照片,在我的照片索引视图中,我列出了所有用户的照片,并允许用户将任何一张照片设置为个人资料照片。用户表有一个名为primary_photo_id的列,用于保存此ID,我使用link_to来设置它:

=link_to "Make this your profile photo", user_path(@user, :user => {:primary_photo_id  => "#{photo.id}"}), :method => :put

问题是before_filter启动并将阻止它工作,因为通过params [:id]检索的@user失败,因为它不是正确的参数。如果我删除了before_filter,它工作正常,但它不再检查正确的用户了。

(第二个有点相关的问题是为什么上面的代码可以工作但是这个:

=link_to "Make this your profile photo", user_path(@user, :primary_photo_id  => "#{photo.id}"), :method => :put

感谢。我对rails和编程很陌生,所以你可以对我的具体问题说些什么,以及我在这里用代码做的任何不好的做法,都非常感激。

1 个答案:

答案 0 :(得分:2)

之前的过滤器(#correct_user)在#update之前运行,因此如果您未在过滤器中进行比较,则尚未设置实例变量@user在过滤器之前的另一个您的执行顺序如下:

  1. 运行#correct_user - 将current_user与@user进行比较(如果未设置,则为nil)。如果用户没有登录,那么这些只会匹配我在猜测
  2. 假设我们通过了,运行#update并查找@user
  3. 解决问题的最简单方法可能就是将@user查询移到之前的过滤器中:

    before_filter :correct_user,  :only => [:edit, :update, :destroy]
    
    def edit
      # .. as before, but no need to look up user first
    end
    
    def update
      if @user.update_attributes(params[:user])
        redirect_to current_user, :notice => "User updated!"
      else
        redirect_to current_user, :notice => "User not updated. waa waa."
      end
    end
    
    def destroy
      # .. as before, but no need to look up user first
    end
    
    private
    
    def correct_user
      @user = User.find(params[:id])
      if current_user != @user        
        redirect_to root_url, :notice => "Cannot act on different user."
      end
    end
    

    由于@user现在位于过滤器中,因此无需在每个控制器操作中再次查找它。希望这有帮助!