エラー処理
Rust アプリケーションにおける一般的なエラー処理手法
Rustのエラー処理はJavaなどの言語とは異なり、try...catchのような仕組みは存在しません。一般的な手法は、アプリケーションレベルでグローバルなエラー処理型を定義することです:
ここではthiserrorライブラリを使用しており、独自のカスタムエラー型を簡単に定義し、コードを簡潔に記述できます。記述を簡略化するために、AppResultも併せて定義しています。
thiserror と anyhow
Rustのエラー処理エコシステムでは、thiserrorとanyhowがよく使用されるライブラリです:
-
thiserror: ライブラリ開発者向けで、明確なエラー型を定義するために使用されます。派生マクロを通じてカスタムエラー型に
std::error::Errorトレイトを実装するのを支援し、エラーの表現方法を定義できます。ライブラリを構築する場合や、ユーザーに明確なエラー型を提供する必要がある場合、thiserrorがより適しています。 -
anyhow: アプリケーション開発者向けで、汎用的なエラー型
anyhow::Errorを提供します。これはstd::error::Errorトレイトを実装したあらゆるエラーを含むことができます。エラーの定義よりも伝播に重点を置いており、特にアプリケーション層のコードに適しています。様々なエラーをanyhow::Errorに迅速に変換でき、ボイラープレートコードの記述を減らすことができます。
一部のシナリオでは、これら2つのライブラリを併用することがあります:ライブラリではthiserrorでエラー型を定義し、アプリケーションではanyhowでこれらのエラーを処理・伝播します。
Handlerにおけるエラー処理
Salvoでは、Handlerも様々なエラーに遭遇することがよくあります。例えば:データベース接続エラー、ファイルアクセスエラー、ネットワーク接続エラーなどです。この種のエラーに対しては、前述のエラー処理手法を採用できます:
ここでのhomeは直接AppResult<()>を返しています。しかし、このエラーはどのように表示されるべきでしょうか?AppResultというカスタムエラー型にWriterを実装する必要があり、この実装の中でエラーの表示方法を決定できます:
SalvoのHandlerはResultを返すことができ、Result内のOkとErrの型が両方ともWriterトレイトを実装している必要があります。
anyhowを使用したエラー処理
anyhowの使用が比較的広まっていることを考慮し、Salvoはanyhow::Errorに対する組み込みサポートを提供しています。anyhow機能を有効にすると、anyhow::ErrorはWriterトレイトを実装し、InternalServerErrorにマッピングされます:
anyhow機能を使用するには、Cargo.tomlでSalvoのanyhow featureを有効にする必要があります:
これにより、ハンドラ関数は直接anyhow::Result<T>を返せるようになります:
Errorにはしばしば機密情報が含まれており、一般的には一般ユーザーに見せたくないものです。そうでなければあまりにも安全ではなく、プライバシーも少しもなくなってしまいます。しかし、開発者やサイト管理者であれば、考え方は異なるかもしれません。エラーがすべてをさらけ出し、最も真実のエラー情報を見せてほしいと望むでしょう。
writeメソッドでは、実際にRequestとDepotの参照を取得できることがわかります。これにより、上記のような巧妙な操作を簡単に実装できます:
エラーページの表示
Salvoに組み込まれているエラーページは、ほとんどの場合で要件を満たしています。リクエストのデータタイプに応じてHtml、Json、またはXmlページを表示できます。しかし、一部の場合では、依然としてエラーページの表示をカスタマイズしたいと考えるでしょう。
これはカスタムCatcherを実装することで実現できます。詳細な説明はCatcherのセクションを参照してください。