wrongwrongな開発日記

しんまいさんの忘備録

【SpringBoot】内部エラー周りの話

内部エラー周りの設計について自分の経験から得た知見をまとめます(なのでセオリーとかアンチパターンとか有れば教えていただけるとありがたいです)。
内部エラーとは基本的に自分で書いてThrowするようなエラーを想定しています。

エラーハンドリングに関してはサンプル記事が溢れているので省略します。

例外クラスの作り方

サーバーとフロント側で「エラー内容/HTTPステータスコードによって大体の挙動を統一する」ことができるため、基本的に返却するHTTPステータスコードに合わせて例外クラスを作ればよいと思います。
内部エラーコードを設定することも考えられますが、アプリケーション規模が小さいうちはそこまでしなくてもHTTPステータスコードに合わせておけば大体の場合に対応できるので、個人的にはYAGNIかなと思っています。

自分のやっているプロジェクトでは以下のような例外クラスを設定していました(抜粋です)。

ステータスコード 例外クラス名 エラー内容 フロントでの対応
400 InvalidFormException フォームの入力エラー 項目ごとにエラー表示
409 ResourceConflictException リソース更新のコンフリクト データの再取得を促すアラートを表示
500 ${プロジェクト名}Exception その他内部的なエラー エラーページの表示

継承元に関してはExceptionではなくRuntimeException系のものを設定した方が後々楽だと思います(ラムダ式内で内部エラーを投げる時に困る場合などが有るため)。

その他Tips的な内容

スタックトレースのフィルタリング

全て1つのプロジェクトで完結している場合、内部エラーは発生箇所がパッケージ内に有ることが確実になるため、それ以外のスタックトレースは基本的に不要となります。
そこで自分はフィルタリング関数を用意して宣言時にスタックトレースを設定し直していました。

フィルタリング関数

static StackTraceElement[] filteringStackTrace(StackTraceElement[] elements) {
    return Arrays.stream(elements)
            .filter(it -> it.getClassName().startsWith("${プロジェクトルートのパッケージ名の文字列}"))
            .toArray(StackTraceElement[]::new);
}

使い方

public class ResourceConflictException extends RuntimeException {
    public ResourceConflictException(String msg) {
        super(msg);

        // スタックトレースはフィルタリングして設定
        setStackTrace(FilteringStackTrace.filteringStackTrace(getStackTrace()));
    }
}

例外処理の結果でJSONを返す場合にJSON以外を返すControllerでproducesを設定してはならない

producesで指定したのと違う内容を返却しようとすることになるため、HttpMediaTypeNotAcceptableExceptioとしてエラーになります。
具体的には以下の記事にまとめてあります。