檢視來源 錯誤和例外處理

與任何其他 Elixir 程式碼一樣,在 LiveView 生命週期中可能會發生例外處理。本頁描述 LiveView 在不同階段如何處理錯誤。

預期的範例

在此部分,我們將討論您預期在應用程式中發生的錯誤情況。例如,使用者填寫資料無效的表單是可以預期的。在 LiveView 中,我們通常透過將表單狀態儲存在 LiveView 指派項目和將任何相關錯誤訊息呈示回 client 端來處理這些案例。

我們也可以使用 flash 訊息來這麼做。例如,假設您有一個頁面用於管理組織中的所有「團隊成員」。但是,如果組織中只剩下一個成員,他們就不可以被允許離開。您可能會想要使用 flash 訊息來處理這個問題

if MyApp.Org.leave(socket.assigns.current_org, member) do
  {:noreply, socket}
else
  {:noreply, put_flash(socket, :error, "last member cannot leave organization")}
end

但是,有人可能會主張,如果組織中最後一個成員不能離開它,最好在組織只有單一成員時,不要在 UI 中顯示「離開」按鈕。

由於按鈕沒有出現在 UI 中,組織中只有單一成員時觸發「離開」動作,這是個預期之外的範例。這表示我們可以將上述程式碼改寫為

true = MyApp.Org.leave(socket.assigns.current_org, member)
{:noreply, socket}

如果 leave 沒有傳回 true,Elixir 會引發 MatchError 例外處理,或者您可以提供一個會引發特定例外處理的 leave! 函式

MyApp.Org.leave!(socket.assigns.current_org, member)
{:noreply, socket}

但是,如果發生例外處理,會怎麼影響 LiveView 呢?讓我們討論預期之外的範例。

預期之外的範例

Elixir 開發人員傾向於撰寫斷言程式碼。這表示如果我們預期 leave 一律傳回 true,我們可以像上面那樣針對其結果進行明確配對

true = MyApp.Org.leave(socket.assigns.current_org, member)
{:noreply, socket}

如果 leave 失敗並傳回 false,會引發例外處理。對於 Elixir 開發人員來說,在他們的 Phoenix 應用程式中針對預期之外的範例使用例外處理是很常見的。

例如,如果您正在建置一個使用者可能屬於一個或多個組織的應用程式,在存取組織頁面時,您可能會想要像這樣檢查使用者是否有權限存取此頁面

organizations_query = Ecto.assoc(socket.assigns.current_user, :organizations)
Repo.get!(organizations_query, params["org_id"])

上述程式碼建構的查詢會回傳所有屬於目前使用者的組織,然後驗證所提供的 org_id 是否屬於該使用者。如果沒有這樣的 org_id 或使用者無法存取,Repo.get! 將引發 Ecto.NoResultsError 例外。

在一般控制器要求期間,此例外將轉換成 404 例外並呈現在自訂錯誤頁面中,如 在此詳細說明。LiveView 會以三種不同的方式對例外做出反應,視其在生命週期中所處的位置而定。

HTTP 安裝期間的例外

第一次存取 LiveView 時,會將一般 HTTP 要求傳送至伺服器,並由 LiveView 處理。mount 回呼會被呼叫,然後會描繪頁面。此處的任何例外都會被 Phoenix 錯誤檢視捕捉、記錄並轉換成例外頁面 - 其運作方式與控制器完全相同。

已連線安裝期間的例外

如果初始的 HTTP 要求成功,LiveView 將使用有狀態連線(通常是 WebSocket)連接至伺服器。這會在伺服器上產生一個長執行且輕量級的 Elixir 程序,呼叫 mount 回呼並描繪已更新版本的頁面。

此階段期間的例外會導致 LiveView 程序崩潰,並會記錄該崩潰。一旦用戶端察覺到崩潰,就會完全重新載入網頁。這會導致在一般 HTTP 要求期間再次呼叫 mount(前一小節的完全相同情境)。

換句話說,LiveView 會在錯誤時重新載入頁面,使其失敗,彷彿 LiveView 從未參與描繪。

已連線安裝之後的例外

在 LiveView 安裝並連線之後,任何錯誤都會導致 LiveView 程序崩潰並記錄該崩潰。一旦用戶端察覺到錯誤,它將會重新安裝 LiveView,透過有狀態連線,而不重新載入頁面(前一小節的完全相同情境)。如果重新安裝成功,LiveView 會回到工作狀態,更新頁面並向使用者顯示最新資訊。

舉例來說,假如同時有兩個使用者試著離開組織。在此情況下,兩位都會看到「離開」按鈕,但我們的 leave 函式呼叫只會對其中一位使用者成功

true = MyApp.Org.leave(socket.assigns.current_org, member)
{:noreply, socket}

當例外引發時,用戶端會重新安裝 LiveView。重新安裝後,您的程式碼現在會注意到組織中只有一個使用者,因此不再顯示「離開」按鈕。換句話說,透過重新安裝,我們經常更新頁面的狀態,允許例外自動處理。

請注意,「如果」與代碼 leave 函數的結果條件檢查或僅宣告回傳 true 完全由您決定。如果所有人員同時離開組織的可能性很低,那麼您不妨將其視為意外情況。雖然其他開發人員會更習慣明確處理這些情況。在這兩種情況下,LiveView 都能支援您的需求。