在非原创性方面,我正在使用Ruby on Rails编写博客应用程序。我的PostsController
包含一些代码,可确保登录用户只能编辑或删除自己的帖子。
我尝试将此代码分解为一个私有方法,其中只有一个参数可以显示flash消息,但是当我这样做并通过编辑其他作者的帖子进行测试时,我得到了ActionController::DoubleRenderError
- “只能每个动作渲染或重定向一次“。
如何保留这些检查DRY?显而易见的方法是使用前置过滤器,但destroy
方法需要显示不同的闪存。
以下是相关的控制器代码:
before_filter :find_post_by_slug!, :only => [:edit, :show]
def edit
# FIXME Refactor this into a separate method
if @post.user != current_user
flash[:notice] = "You cannot edit another author’s posts."
redirect_to root_path and return
end
...
end
def update
@post = Post.find(params[:id])
# FIXME Refactor this into a separate method
if @post.user != current_user
flash[:notice] = "You cannot edit another author’s posts."
redirect_to root_path and return
end
...
end
def destroy
@post = Post.find_by_slug(params[:slug])
# FIXME Refactor this into a separate method
if @post.user != current_user
flash[:notice] = "You cannot delete another author’s posts."
redirect_to root_path and return
end
...
end
private
def find_post_by_slug!
slug = params[:slug]
@post = Post.find_by_slug(slug) if slug
raise ActiveRecord::RecordNotFound if @post.nil?
end
答案 0 :(得分:2)
之前的过滤方式仍然是一个不错的选择。您可以使用控制器的action_name
方法访问请求的操作。
before_filter :check_authorization
...
protected
def check_authorization
@post = Post.find_by_slug(params[:slug])
if @post.user != current_user
flash[:notice] = (action_name == "destroy") ?
"You cannot delete another author’s posts." :
"You cannot edit another author’s posts."
redirect_to root_path and return false
end
end
对不起那个中间那个三元运营商。 :)当然,你可以做任何你喜欢的逻辑。
如果您愿意,也可以使用方法,如果失败则通过显式返回来避免双重渲染。这里的关键是返回,这样你就不会双重渲染。
def destroy
@post = Post.find_by_slug(params[:slug])
return unless authorized_to('delete')
...
end
protected
def authorized_to(mess_with)
if @post.user != current_user
flash[:notice] = "You cannot #{mess_with} another author’s posts."
redirect_to root_path and return false
end
return true
end
您可以通过拆分行为的不同部分(授权,处理错误授权)来更简化(在我看来):
def destroy
@post = Post.find_by_slug(params[:slug])
punt("You cannot mess with another author's post") and return unless author_of(@post)
...
end
protected
def author_of(post)
post.user == current_user
end
def punt(message)
flash[:notice] = message
redirect_to root_path
end
就个人而言,我更喜欢将所有这些例行工作卸载到插件中。我个人最喜欢的授权插件是Authorization。在过去的几年里,我用它取得了巨大的成功。
这将重构您的控制器以使用变体:
permit "author of :post"
答案 1 :(得分:1)
简单的答案是将消息更改为适合两者的内容:“你不能弄乱其他作者的帖子。”
答案 2 :(得分:1)
如果您不喜欢最后一个解决方案中的丑陋*返回,您可以使用around过滤器并仅在用户获得授权时有条件地生成。
around_filter :check_authorization, :only => [:destroy, :update]
private
def check_authorization
@post = Post.find_by_slug(params[:slug])
if @post.user == current_user
yield
else
flash[:notice] = case action_name
when "destroy"
"You cannot delete another author's posts."
when "update"
"You cannot edit another author's posts."
end
redirect_to root_path
end
end
* - 这是我的偏好,虽然代码方面它完全有效。我只是发现风格方面,它往往不合适。
我还应该补充一点,我没有对此进行测试,并且我不是100%肯定它会起作用,尽管它应该很容易尝试。