在Ruby on Rails中处理无ID错误的最佳方法是什么?

时间:2013-02-14 19:37:57

标签: ruby-on-rails ruby ruby-on-rails-3 ruby-on-rails-3.2

在我的控制器中,我使用了类似的东西来验证project是否真的属于给定的user

private

def authorized_user
  @project = Project.find(params[:id])
  redirect_to root_path unless current_user?(@project.user)
end

这很有效,因为用户A无法看到用户B的项目(他正在转发到根页面)。

但是,只有在请求确实存在的project网址时,此功能才有效。

例如,URL http://localhost:3000/projects/1将显示用户的项目或转发到根URL(如果其他用户尝试访问该项目)。

但是当我尝试访问数据库中根本不存在的项目时,例如像这样:

http://localhost:3000/projects/777

...我收到一个丑陋的ActiveRecord::RecordNotFound错误:

Couldn't find Person with id=777

在这里改善用户体验的最佳方法是什么?

我还没有实际部署一个Rails项目,所以我甚至不知道这个错误在生产模式下会是什么样子。

有人可以帮忙吗?

...谢谢

4 个答案:

答案 0 :(得分:5)

我个人喜欢用这个:

@project = Project.where(id: params[:id]).first

如果项目不存在,@project将为零。

答案 1 :(得分:2)

根据您的处理方式,您可以使用

@project = Project.find_by_id(params[:id])

如果没有找到记录,这将使@project为零,并且您将手动处理该案例。

另一个解决方案是抛出一个有意义的404,因为资源不存在。您可以使用以下命令在任何控制器(或应用程序控制器)中轻松完成此操作:

rescue_from ActiveRecord::RecordNotFound, :with => :not_found

def not_found
  raise ActionController::RoutingError.new('Not Found')
end

这会导致类似:

class ApplicationController < ..
  rescue_from ActiveRecord::RecordNotFound, :with => :not_found

  def not_found
    raise ActionController::RoutingError.new('Not Found')
  end
end

后一种解决方案将向用户显示默认的404(NOT FOUND)错误页面。在第一种情况下,你有更多的控制权,但代价是无处不在

希望有所帮助。

答案 2 :(得分:1)

首先,为避免未授权用户访问项目,您应该确定查找方法的范围:

current_user.projects.find(params[:id])

这样,您将在开发过程中遇到“无法找到带ID的项目”错误。为避免这种情况,您可以使用:

current_user.projects.find_by_id(params[:id])

返回nil而不是异常,但有充分的理由说明你通常不应该这样做。在一个编写良好的rails应用程序中,任何用户访问项目的唯一时间他不应该是他手动更改URL中的id。您希望在日志中报告此内容,而不是以静默方式跳过。

最后,要投掷403 Forbidden而不是404 Not Found,您可以考虑使用众多授权宝石中的一种(Ryan Bates想到cancan)。

编辑:哦,在制作中,ActiveRecord :: RecordNotFound将呈现404.html页面,例如,这些不是您正在寻找的项目。

答案 3 :(得分:1)

尝试:

class ApplicationController < ActionController::Base
  rescue_from ActiveRecord::RecordNotFound, :with => :render_404
  # Render 404 page when record not found
    def render_404      
       render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
    end
end