ruby-on-rails - 在 Rails 中处理异常和错误的最佳策略是什么?

标签 ruby-on-rails exception exception-handling

我想知道人们是否会分享他们处理异常和错误的最佳实践/策略。现在我不是问什么时候抛出异常(这里已经彻底回答了:SO: When to throw an Exception)。而且我没有在我的应用程序流中使用它 - 但总是有合法的异常(exception)发生。例如,最流行的是 ActiveRecord::RecordNotFound。处理它的最佳方法是什么?干燥的方式?

现在我在我的 Controller 中做了很多检查,所以如果 Post.find(5)返回 Nil - 我检查并抛出一个闪现消息。然而,虽然这是非常细粒度的 - 从某种意义上说有点麻烦,我需要检查每个 Controller 中的异常,而它们中的大多数本质上是相同的,并且与未找到记录或未找到相关记录有关 - 例如作为 Post.find(5)未找到,或者如果您尝试显示与不存在的帖子相关的评论,则会引发异常(类似于 Post.find(5).comments[0].created_at )

我知道你可以在 ApplicationController 中做这样的事情,然后在特定的 Controller /方法中覆盖它以获得更细粒度的支持,但是这是一个正确的方法吗?

class ApplicationController < ActionController::Base
    rescue_from ActiveRecord::RecordInvalid do |exception|
        render :action => (exception.record.new_record? ? :new : :edit)
    end
end

这也适用于 Post.find(5)没有找到,但是 Post.find(5).comments[0].created_at 呢? - 我的意思是,如果帖子存在但没有评论,我不能抛出一个完整的异常,对吗?

总结到目前为止,我正在使用 if/else/unless 或 case/when(我承认偶尔开始/救援)进行大量手动检查并检查零?或空?等,但似乎必须有更好的方法。

回复:

@米兰:
嗨米兰
感谢您的回复 - 我同意您所说的,我想我误用了异常(exception)这个词。我的意思是现在我做了很多事情,比如:
if Post.exists?(params[:post_id])
    @p = Post.find(params[:post_id])
else
    flash[:error] = " Can't find Blog Post"
end

我做了很多这种“异常处理”,我尽量避免使用开始/救援。但在我看来,这是一个足够常见的结果/验证/情况,应该有一个 DRYer 方法来做到这一点,不是吗?
你会怎么做这种检查?

在这种情况下如何处理?
假设您想在 View 中显示评论创建日期:
Last comment for this post at : <%= @post.comments[0].created_at %>

而且这篇文章没有任何评论。
你可以做
Last comment for this post at : <%= @post.comments.last.created_at unless @post.comments.empty? %>

你可以做一个检查 Controller 。等等。有几种方法可以做到。但是处理这个问题的“最佳”方法是什么?

最佳答案

你做的事实很多手动检查异常的结果表明您只是没有正确使用它们。事实上,你的例子中没有一个是异常(exception)的。

至于不存在的帖子 - 您应该期望您的 API 用户(例如,通过浏览器使用您的网络的用户)请求不存在的帖子。

你的第二个例子(Post.find(5).comments[0].created_at) 也不异常(exception)。有些帖子只是没有评论,而您事先就知道了。那么为什么要抛出异常呢?

ActiveRecord::RecordInvalid 示例的情况也是如此。没有理由通过异常处理这种情况。用户在表单中输入一些无效数据是很常见的事情,没有什么特别之处。

对这些情况使用异常机制在某些情况下可能非常方便,但由于上述原因,这是不正确的。

话虽如此,这并不意味着您不能 DRY 封装这些情况的代码。至少在某种程度上,您很有可能做到这一点,因为这些是非常常见的情况。

那么,异常(exception)情况呢?嗯,第一条规则确实是:尽可能少地使用它们。

如果你真的需要使用它们,一般有两种异常(exception)情况(如我所见):

  • 不会破坏用户在您的应用程序中的一般工作流程的异常(想象一下您的个人资料图片缩略图生成例程中的异常),您可以对用户隐藏它们,也可以在必要时通知他问题及其后果
  • 阻止用户使用该应用程序的异常(exception)情况。这些是最后的手段,应该通过 Web 应用程序中的 500 内部服务器错误来处理。

  • 我倾向于使用 rescue_from ApplicationController 中的方法仅适用于后者,因为第一种有更合适的位置,而 ApplicationController 作为 Controller 类的最顶层似乎是在这种情况下回退到的正确位置(尽管现在某种 Rack 中间件可能更适合放置这样的东西)。

    -- 编辑 --

    build 性部分:

    至于第一件事,我的建议是开始使用 find_by_id 而不是 find,因为它不会抛出异常,但如果不成功则返回 nil。你的代码看起来像这样:
    unless @p = Post.find_by_id(params[:id])
      flash[:error] = "Can't find Blog Post"
    end
    

    这远不那么健谈。

    DRYing 这种情况的另一个常见习惯用法是使用 Controller before_filters 来设置常用变量(如本例中的 @p)。之后,您的 Controller 可能如下所示
    controller PostsController
      before_filter :set_post, :only => [:create, :show, :destroy, :update]
    
      def show
          flash[:error] = "Can't find Blog Post" unless @p
      end 
    
    private
    
      def set_post
        @p = Post.find_by_id(params[:id]) 
      end
    
    end
    

    至于第二种情况(不存在的最后一条评论),这个问题的一个明显解决方案是将整个事情移动到一个助手中:
    # This is just your way of finding out the time of the last comment moved into a 
    # helper. I'm not saying it's the best one ;)
    def last_comment_datetime(post)
      comments = post.comments
      if comments.empty?
        "No comments, yet."
      else
        "Last comment for this post at: #{comments.last.created_at}"
      end
    end
    

    然后,在您看来,您只需调用
    <%= last_comment_datetime(post) %>
    

    通过这种方式,边缘情况(不带任何评论的帖子)将在它自己的位置进行处理,并且不会使 View 困惑。

    我知道,这些都没有暗示在 Rails 中处理错误的任何模式,但也许通过一些重构,例如这些,您会发现对某种异常/错误处理策略的大量需求就消失了。

    关于ruby-on-rails - 在 Rails 中处理异常和错误的最佳策略是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2608325/

    相关文章:

    exception-handling - Yii2 如何处理 Swift_TransportException 异常?

    ruby-on-rails - rails : How to make available parent attribute in setter method

    ruby-on-rails - Rails 远程与 Bootstrap 远程模式冲突

    Laravel 5 错误处理

    C# 内联检查语句不起作用

    asp.net-mvc-3 - ASP.NET MVC 中全局错误/异常处理的最佳实践是什么?

    ruby-on-rails - 如何使用 Rails Form 和 Role Model Gem 添加多个角色

    ruby-on-rails - bundle 命令错误的 mysql2 gem(总是 0.3.2)

    java - 是否可以在异常后恢复 Java 执行?

    C# - 如何使用反射调用参数数量可变的静态方法