錯誤處理
Rust 應用中的常規錯誤處理方式
Rust 的錯誤處理有別於 Java 等語言,它沒有 try...catch 這類語法,常見做法是在應用程式層面定義全域的錯誤處理類型:
此處使用了 thiserror 函式庫,它能便捷地定義自訂錯誤類型以簡化程式碼。為書寫簡潔,同時定義了 AppResult 型別別名。
thiserror 與 anyhow
在 Rust 錯誤處理生態中,兩個常用的函式庫是 thiserror 和 anyhow:
-
thiserror:適用於函式庫開發者,用於定義清晰的錯誤類型。透過派生巨集協助您為自訂錯誤類型實現
std::error::Errortrait,同時允許定義錯誤的呈現方式。當您建置函式庫或需要為使用者提供明確錯誤類型時,thiserror是較佳選擇。 -
anyhow:適用於應用程式開發者,提供通用錯誤類型
anyhow::Error,能容納任何實現std::error::Errortrait 的錯誤。它更側重於錯誤傳播而非定義,特別適合應用層程式碼,可快速將各類錯誤轉換為anyhow::Error,減少樣板程式碼的編寫需求。
某些場景中,您可能會同時使用這兩個函式庫:在函式庫中使用 thiserror 定義錯誤類型,在應用程式中使用 anyhow 處理和傳播這些錯誤。
Handler 中的錯誤處理
在 Salvo 中,Handler 也常會遭遇各種錯誤,例如:資料庫連接錯誤、檔案存取錯誤、網路連接錯誤等。針對這類錯誤,可採用前述的錯誤處理手法:
此處的 home 直接回傳了 AppResult<()>。但這個錯誤該如何顯示呢?我們需要為自訂錯誤類型 AppResult 實現 Writer trait,在此實現中可決定錯誤的顯示方式:
Salvo 中的 Handler 可回傳 Result,只需確保 Result 中的 Ok 與 Err 類型皆實現 Writer trait。
使用 anyhow 進行錯誤處理
考慮到 anyhow 的廣泛使用,Salvo 提供了對 anyhow::Error 的內建支援。啟用 anyhow 功能後,anyhow::Error 會實現 Writer trait,並被映射為 InternalServerError:
要使用 anyhow 功能,需在 Cargo.toml 中啟用 Salvo 的 anyhow feature:
如此一來,您的處理函式就能直接回傳 anyhow::Result<T>:
Error 中常包含敏感資訊,一般情況下不希望被普通使用者看見,否則既不安全也毫無隱私可言。但若您是開發人員或網站管理員,想法可能截然不同——您會希望錯誤能「褪去外衣」,展現最真實的錯誤訊息。
可以看到,在 write 方法中,我們實際上能取得 Request 和 Depot 的參考,這讓實現上述特殊操作變得十分便利:
錯誤頁面的顯示
Salvo 內建的錯誤頁面在大多數情況下能滿足需求,它會根據請求的資料類型顯示 Html、Json 或 Xml 頁面。然而,某些情境中我們仍希望自訂錯誤頁面的顯示方式。
這可透過自訂 Catcher 實現,詳細說明請參閱 Catcher 章節。