簡體   English   中英

在Rails中處理異常和錯誤的最佳策略是什么?

[英]What is best strategy to handle exceptions & errors in Rails?

我想知道人們是否會分享他們處理異常和錯誤的最佳實踐/策略。 現在我不是在問什么時候拋出異常(這里已經得到了很好的回答:所以:什么時候拋出異常 )。 我並沒有將它用於我的應用程序流程 - 但是有合理的例外情況一直在發生。 例如,最受歡迎的是ActiveRecord :: RecordNotFound。 處理它的最佳方法是什么? 干嘛?

現在我在我的控制器中做了很多檢查,所以如果Post.find(5)返回Nil - 我檢查並發出一條flash消息。 然而,雖然這是非常精細的 - 在某種意義上我需要檢查每個控制器中的異常,但它們中的大多數基本相同並且與未找到的記錄或未找到的相關記錄有關 - 這樣如果找不到Post.find(5)或者如果你試圖顯示與不存在的帖子相關的注釋,那么會拋出異常(比如Post.find(5).comments[0].created_at

我知道你可以在ApplicationController中執行類似的操作,稍后在特定的控制器/方法中覆蓋它以獲得更細粒度的支持,但這是否是一種正確的方法呢?

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怎么Post.find(5).comments[0].created_at我的意思是如果帖子存在但是沒有,我就不能拋出一個完整的異常評論,對嗎?

總結到目前為止,我正在使用if / else / except或者case / when(我偶爾承認開始/救援)並檢查nil進行了大量的手動檢查? 或空?等等,但似乎必須有更好的方式。

回復:

@Milan:您好米蘭感謝您的回復 - 我同意您所說的話,我認為我誤用了例外。 我的意思是,現在我做了很多事情,比如:

if Post.exists?(params[:post_id])
    @p = Post.find(params[:post_id])
else
    flash[:error] = " Can't find Blog Post"
end

我做了很多這種“異常處理”,我盡量避免使用開始/救援。 但在我看來,這是一個足夠普遍的結果/驗證/情況,應該有一個DRYer方法來做到這一點,不是嗎? 你會怎么做這種檢查?

在這種情況下如何處理呢? 假設您要在視圖中顯示評論創建日期:

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? %>

你可以辦理入住手續。 等等。有幾種方法可以做到這一點。 但處理這個問題的“最佳”方法是什么?

您對異常進行大量手動檢查的事實表明您沒有正確使用它們。 事實上,你的例子都不例外。

對於不存在的帖子 - 您應該期望您的API用戶(例如,通過瀏覽器使用您的網站的用戶)要求不存在的帖子。

你的第二個例子(Post.find(5).comments [0] .created_at)也不例外。 有些帖子沒有評論,你事先就知道了。 那么為什么要拋出異常呢?

ActiveRecord :: RecordInvalid示例的情況也是如此。 沒有理由通過例外來處理這種情況。 用戶將一些無效數據輸入表單是很平常的事情,並沒有什么特別之處。

在某些情況下,對這些情況使用異常機制可能非常方便,但由於上述原因,它是不正確的。

話雖如此,但這並不意味着你不能干掉封裝這些情況的代碼。 由於這些是非常常見的情況,因此至少在某種程度上你可以做到這一點的可能性非常大。

那么,例外呢? 嗯,第一個規則確實是:盡可能稀疏地使用它們。

如果你真的需要使用它們,一般有兩種例外(我認為):

  1. 不會破壞用戶在應用程序內部的一般工作流程的異常(想象一下您的個人資料圖片縮略圖生成例程中的異常),您可以將其隱藏在用戶之外,或者只是在需要時通知他有關問題及其后果的問題

  2. 阻止用戶完全使用該應用程序的異常。 這是最后的手段,應該通過Web應用程序中的500內部服務器錯誤來處理。

我傾向於僅在后者中使用ApplicationController中的rescue_from方法,因為第一種類型的適當位置和ApplicationController作為控制器類的最頂層似乎是在這種情況下回歸的正確位置(盡管如今某種Rack中間件可能更適合放置這樣的東西)。

- 編輯 -

建設性的部分:

至於第一件事,我的建議是開始使用find_by_id而不是find,因為它不會拋出異常,但如果不成功則返回nil。 您的代碼看起來像這樣:

unless @p = Post.find_by_id(params[:id])
  flash[:error] = "Can't find Blog Post"
end

這不是很健談。

干這種情況的另一個常見習慣是使用控制器before_filters來設置常用變量(在本例中為@p)。 之后,您的控制器可能如下所示

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) %>

通過這種方式,邊緣情況(沒有任何注釋的帖子)將在它自己的位置處理,並且不會使視圖混亂。

我知道,這些都沒有表明在Rails中處理錯誤的任何模式,但也許有一些重構,比如這些,你會發現對異常/錯誤處理的某種策略的大量需求消失了。

例外情況適用於特殊情況。 糟糕的用戶輸入通常不是例外; 如果有的話,這很常見。 當您遇到特殊情況時,您希望盡可能多地提供自己的信息。 根據我的經驗,最好的方法是根據調試經驗虔誠地改進異常處理。 遇到異常時,首先應該為它編寫一個單元測試。 您應該做的第二件事是確定是否有更多信息可以添加到例外。 在這種情況下,更多信息通常采取以下形式:在堆棧上方捕獲異常並處理它或拋出一個新的,更具信息性的異常,這種異常具有額外的上下文。 我的個人規則是,我不喜歡從堆棧中的三個級別捕獲異常。 如果異常必須遠遠超過該異常,則需要提前捕獲它。

至於在UI中暴露錯誤,只要你沒有將它們嵌套得太深, if / case語句就完全沒問題。 就是這種代碼難以維護的時候。 如果它成為問題,你可以抽象它。

例如:

def flash_assert(conditional, message)
  return true if conditional
  flash[:error] = message
  return false
end

flash_assert(Post.exists?(params[:post_id]), "Can't find Blog Post") or return

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM