例外処理方針⚓︎
バックエンドアプリケーションで発生するシステム例外や業務例外は、例外フィルターによって捕捉します。 例外フィルターでは、ログ出力方針に従ってアプリケーションログを出力します。
システム例外を例外フィルターで捕捉した場合は、 HTTP 500 のエラーレスポンスをフロントエンドアプリケーションに返却します。 フロントエンドアプリケーションは、システムエラー画面に遷移し、 Sorry メッセージを出力します。 フロントエンドアプリケーション内で例外が発生した場合も、同様にシステムエラー画面に遷移します。
業務例外を例外フィルターで補足した場合は、 HTTP 400 のエラーレスポンスをフロントエンドアプリケーションに返却します。 フロントエンドアプリケーションは、その画面内のメッセージ領域にエラーメッセージを表示します。
フロントエンドの例外処理方針⚓︎
フロントエンドは、ユーザーが操作する、ログの取得が難しいなど、バックエンドとは異なる特性があります。 しかし例外処理方針としては、バックエンドと同様に正常なフローに復帰できるかどうかを最も重要な観点とし、そのためにどこで例外を捕捉しどのようにユーザーへ通知するかを検討します。
例外の種類⚓︎
フロントエンドの例外は、バックエンドと同様、業務例外とシステム例外の 2 種類に分けて考えます。
業務例外は、業務フローで想定されるエラーを表す例外です。システム例外は、業務フロー上は想定されないシステムのエラーを表す例外です。 API 通信においては、ステータスコードが 40x のエラーを業務例外、 50x のエラーをシステム例外として扱います。
例外の捕捉⚓︎
フロントエンドで発生する例外には以下のようなものが考えられます。
- ユーザーが操作した際に発生する例外
- API 通信で発生する例外
- システムエラー
- ネットワークエラー
例外の発生が予測できる箇所では、同期処理は try-catch
、非同期処理は catch
メソッドを利用します。予期せぬ例外が発生した場合は、 Vue.js の app.config.errorHandler
や JavaScript の window.onerror
といったグローバルエラーハンドリングで例外を捕捉します。
例外の種類 | ハンドリング方法 |
---|---|
Vue アプリケーション内で発生する例外 | app.config.errorHandler |
JavaScript の構文エラーや Vue アプリケーション外の例外 | window.onerror |
非同期処理で発生する例外 | window.onunhandledrejection |
HTTP 通信で発生する例外について、レスポンスやステータスコードを解析する場合は、 Axios の axios.interceptors.response
が有効です。
例外の処理⚓︎
フロントエンドの例外処理では、ユーザーが自身で対応できるか、という観点が重要になります。 たとえばセッションタイムアウトになるといったような場合は、ユーザーが再度ログインすることで対処できます。 一方 Web API サーバー内で予期しない問題が発生した場合、ユーザー自身では問題を解決できず、システム管理者に問い合わせをするようなフローが考えられます。
ユーザーに対しては、自身がどのような対応をできるかを考慮し、適切な通知方法を選択します。ポップアップ等の画面遷移を伴わずに通知するか、エラーページへ遷移するかは、ユーザーが操作を継続できるかどうかによって選択します。 一方開発者向けの通知では、エラーの詳細を把握するために必要な情報を収集することが重要です。開発環境であればコンソールログに出力し、本番環境ではログ収集ツールに送信する、などが考えられます。 ただしコンソールログへの出力は原則開発環境のみとします。
API 通信で発生する例外処理フロー⚓︎
フロントエンドで最も多く発生する例外は、 API 通信に関連するものです。これはネットワークエラーやサーバーエラー、ユーザーの入力エラーなど様々な要因によって引き起こされます。
フロントエンドでの例外の処理は、通知方法や画面遷移などシナリオによって様々なので、 API 通信の呼び出し元で個別に処理する必要があります。 たとえばリクエストに不備がある、といったユーザーが対応できるようなエラーについては、対応方法をユーザーに通知します。 一方で認証エラーやネットワークエラーのような共通の対応方法が考えられる場合には、特定のエラー処理の仕組み(カスタムエラーなど)を使って処理を一元化する方法が考えられます。
通知方法は、ユーザーの入力を阻害しないようなポップアップやトースト通知などを選択することが望ましいです。ただし、状況によってはエラー画面に遷移する必要がある場合もあります。シナリオに合わせて最適な方法を選択し、ユーザーにわかりやすくエラーを伝えることが重要です。
また、発生したエラーの解析には、以下の方法が考えられます。
- axios.interceptors.response でエラー情報を解析し、適切なカスタムエラーを返却する。
- エラー番号やエラーメッセージを通知し、開発者が問合せるための情報を提供する
- ユーザーの状況やエラー内容をログ収集ツールに送信する
以下は、 API 通信で発生する例外処理のフローです。
sequenceDiagram
participant B as ブラウザ
participant C as Vue コンポーネント
participant A as Axios.Post
participant I as Axios.interceptors.response
participant S as サーバー
B->>C: ボタン押下
activate C
C->>A: API リクエスト呼び出し
activate A
A-)S: API リクエスト
activate S
S--)I: 400 Bad Request
deactivate S
activate I
I->>A: エラー情報の解析
deactivate I
rect rgba(255, 0, 0, 0.5)
A->>C: エラーの伝播
deactivate A
end
C->>B: エラー通知
deactivate C
API 通信のエラーレスポンス⚓︎
API 通信で発生した例外において、バックエンドから返却されるエラーレスポンスには、 ProblemDetails に基づくレスポンスボディーを含みます。
ProblemDetails は、 HTTP API のエラーレスポンスを標準化するための構造体であり、以下のプロパティで定義されています。
項目 | プロパティ名 | 推奨レベル | 内容 |
---|---|---|---|
簡易メッセージ | title | 必須 | 人間が読める形式で提供されます。 |
詳細メッセージ | detail | 本番環境は非推奨 | スタックトレースなどを含めます。開発環境では生産性向上を目的に含めることを推奨しますが、本番環境では使用しません。開発環境と本番環境で構成を変更することが難しい場合には detail を使用しないよう統一することも検討してください。 |
ステータスコード | status | 任意 | HTTP エラーのステータスコードです。 |
詳細 URL | type | 推奨 | エラーを説明する詳細 Web ドキュメントがある場合、ユーザーが参照する URL を追加します。 |
問題が発生したリソースの URI | instance | 任意 | 問題の発生場所を示す URI です。リクエスト先と異なるリソースが問題の発生したリソースである場合、実装の詳細やデータなどの内部情報が漏洩する可能性があるため、追加には注意が必要です。 |
任意のパラメータ | 任意 | 拡張メンバーです。必要に応じて ProblemDetails のプロパティを拡張する場合に利用します。 |
ProblemDetails のレスポンスに含める各プロパティは以下のような観点をもとに取捨選択します。
-
環境による判断
- 本番環境の場合には、ユーザーが見て管理者に伝えるための情報のみをプロパティに含めるべきです。
- 開発環境の場合には、開発者がデバッグやテストなどの作業で確認すべき情報を含めるべきです。
-
API の用途による判断
- 外部公開 API の場合には、セキュリティやプライバシーを考慮し、必要最低限の情報を提供するようにプロパティを設定します。
- 内部 API の場合には、画面に対応したエラーレスポンスとして含めるべきプロパティを設定します。
また、 AlesInfiny Maia OSS Edition では、フロントエンド側で管理しているメッセージを取得するために以下の拡張メンバーを追加で定義しています。
-
exceptionId
例外 ID 。メッセージファイルからエラーメッセージを取得するためのメッセージコードとして利用します。
-
exceptionValues
例外値。例外 ID から取得したエラーメッセージ内のパラメーターに代入する値として利用します。
フロントエンドでポップアップやトースト通知などを用いてエラー通知を実装する場合には、 ProblemDetails の各プロパティの内容を表示します。
エラーレスポンスの例⚓︎
説明したエラーレスポンスの例を以下に示します。
開発環境の場合のエラーレスポンス | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
本番環境の場合のエラーレスポンス | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|