Spring Bootの共通エラーレスポンス設計:Problem+JSON風フォーマットを作る
生徒
「Spring Bootでエラーメッセージを統一した形で返す方法ってありますか?」
先生
「良い視点ですね。REST APIの開発では、共通のエラーレスポンスを設計しておくことで、利用者がとても理解しやすくなります。」
生徒
「なるほど!具体的にはどんなフォーマットが使われてるんですか?」
先生
「最近では、Problem+JSON形式という標準に沿ったフォーマットを参考にした設計がよく使われています。それでは、実装のポイントを一緒に見ていきましょう。」
1. Spring Bootで共通エラーレスポンスを作る理由とは?
Spring BootのREST APIでは、バリデーションエラーや認可エラー、内部サーバーエラーなど様々なエラーが発生します。これらをすべてバラバラの形式で返してしまうと、フロントエンド側やクライアント側が扱いにくくなります。
そこで登場するのが「共通エラーレスポンス設計」です。これにより、すべてのエラーが統一された形式で返され、API利用者はエラーをパースしやすくなります。
その中でも注目されているのが「Problem+JSON」風のフォーマットです。RFC7807という仕様に基づいており、エラー情報をtype・title・status・detail・instanceといった項目で表現します。
2. Problem+JSON風フォーマットの基本構造
Problem+JSON形式は、次のようなJSON構造でエラー情報を返すことを前提としています:
{
"type": "https://example.com/errors/not-found",
"title": "Resource Not Found",
"status": 404,
"detail": "指定されたユーザーが見つかりません。",
"instance": "/api/users/123"
}
この構造のメリットは、API利用者がstatusコードだけでなく、typeやtitleなどでエラーの種類や発生場所を把握しやすくなる点にあります。
3. Spring Bootで共通エラーのレスポンスクラスを作成
まずは、共通のレスポンスとして使うクラスを作成します。Javaで@Builderや@Getterを使えば、簡単に定義できます。
public class ApiError {
private String type;
private String title;
private int status;
private String detail;
private String instance;
// コンストラクタやgetter/setterはLombokでもOK
}
4. @ControllerAdviceを使って例外をグローバルにハンドリング
Spring Bootで例外を一元的に扱うには、@ControllerAdviceを使います。これを使うことで、全コントローラに対する例外処理を共通化できます。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ApiError> handleNotFound(ResourceNotFoundException ex, HttpServletRequest request) {
ApiError error = new ApiError();
error.setType("https://example.com/errors/not-found");
error.setTitle("リソースが見つかりません");
error.setStatus(HttpStatus.NOT_FOUND.value());
error.setDetail(ex.getMessage());
error.setInstance(request.getRequestURI());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// 他の例外も同様に追加可能
}
5. 実際のレスポンス例
たとえば、存在しないユーザーIDを指定してリクエストした場合、次のようなエラーが返されます。
{
"type": "https://example.com/errors/not-found",
"title": "リソースが見つかりません",
"status": 404,
"detail": "ユーザーIDが存在しません。",
"instance": "/api/users/999"
}
これにより、フロントエンド側はレスポンスのstatusやtypeを見て、適切な処理(アラート表示や遷移)を実装できます。
6. Problem+JSON形式に合わせたContent-Typeの指定
RFC7807に準拠するなら、Content-Typeヘッダーにapplication/problem+jsonを指定します。
Spring BootでContent-Typeを返すには、レスポンスエンティティに設定を加えます。
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.contentType(MediaType.valueOf("application/problem+json"))
.body(error);
7. 独自のバリデーションエラーや認可エラーにも対応
MethodArgumentNotValidExceptionなどのバリデーションエラーも、Problem+JSON形式で返すことで、クライアントはdetailの内容を元にフィールドごとのエラーメッセージを表示できます。
また、AccessDeniedExceptionやAuthenticationExceptionに対しても、適切なstatusやtitleを返すように設計しておくと、セキュリティ面での制御がしやすくなります。
8. Spring BootでAPIエラーを美しく扱うコツ
Spring Bootでの共通エラーレスポンス設計は、API開発において非常に重要な要素です。Problem+JSON風の統一フォーマットを用いることで、APIの使いやすさや保守性が飛躍的に向上します。
実装はシンプルですが、利用者にとっては大きなメリットとなるため、ぜひ導入を検討してみてください。