【问题标题】:What is the best way to handle no ID errors in Ruby on Rails?在 Ruby on Rails 中处理无 ID 错误的最佳方法是什么?
【发布时间】:2013-02-14 19:37:57
【问题描述】:

在我的控制器中,我经常使用类似的东西来验证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。

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

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

http://localhost:3000/projects/777

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

Couldn't find Person with id=777

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

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

有人可以帮忙吗?

谢谢...

【问题讨论】:

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


    【解决方案1】:

    我个人喜欢用这个:

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

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

    【讨论】:

    • 好的,谢谢!在这种情况下,first 究竟做了什么?我以为first选择了第一条记录...但我是n00b...
    • 好吧,要纠正你的笨拙,请阅读本指南:Query Interface。你会在那里找到答案。 :)
    • 好的,我明白了。它返回 nil 而不是抛出异常。但是我必须在下一行测试 nil 值......
    • 好的,添加它使它工作得很好:redirect_to root_path unless @person && current_user?(@person.user) 我想我现在必须在我的所有控制器中这样做?
    【解决方案2】:

    试试:

    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
    

    【讨论】:

      【解决方案3】:

      首先,为了避免未经授权的用户访问项目,你应该限定你的 find 方法:

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

      这样你会在开发中得到“Couldn't find Project with id”错误。为避免这种情况,您可以使用:

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

      它返回nil 而不是异常,但有充分的理由说明你通常不应该这样做。在编写良好的 Rails 应用程序中,任何用户唯一会访问他不应该访问的项目的时间是当他手动更改 url 中的 id 时。您希望在日志中报告这一点,而不是默默地跳过。

      最后,要抛出 403 Forbidden 而不是 404 Not Found,您可以考虑使用许多授权 gem 之一(想到了 Ryan Bates 的cancan)。

      编辑:哦,在生产中,ActiveRecord::RecordNotFound 将呈现 404.html 页面,也就是这些不是您正在寻找的项目。

      【讨论】:

      • 您在这里提出的观点非常好。非常感谢你的帮助!哇,在这个帖子中有很多非常好的答案可供选择......
      • 只是为了明确将响应重定向到 404.html - 它们是如何更改默认行为的好例子,但在这种情况下不需要它们。正如我所说,在生产中,Rails 默认会这样做。而在开发中,您希望获取异常页面,而不是 404 页面。
      【解决方案4】:

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

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

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

      另一种解决方案是抛出一个 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(未找到)错误页面。在第一种情况下,你有更多的控制权,但代价是到处都这样做

      希望对您有所帮助。

      【讨论】:

      • 我认为您的第二个解决方案要好得多,因为它不涉及修改脚手架生成的代码,并且可以在应用程序控制器中只用几行代码就可以干净地实现。
      • 你的意思是塞尔吉奥的解决方案更好?好的,但是如何在应用程序控制器中添加它?
      猜你喜欢
      • 2011-05-06
      • 1970-01-01
      • 2017-11-13
      • 2010-09-20
      • 2012-03-21
      • 1970-01-01
      • 1970-01-01
      • 2015-06-12
      • 2017-12-26
      相关资源
      最近更新 更多