1. test 1
  2. test 2
  3. test 3
  4. test 4
  5. test 5
  6. test 6
コース一覧
Spring Boot入門
エラーレスポンスの形を決める

Spring Boot入門

Java で Web API サーバーを作るための Spring Boot の入門コースです。DI コンテナ、REST コントローラ、リクエスト検証、レイヤ分割、Spring Data JPA、例外ハンドリング、テストまでを扱います。毎レッスン、Spring が裏側でやっている判断そのものを pure Java の関数として自分で書き、フレームワークが何を肩代わりしてくれているのかを手触りで理解できる構成にしました。約 12 時間 (1 日 30 分 × 24 日) で 48 レッスンを修了できます。

1
Spring Bootとは何か
01. Spring Bootで何が作れるのか10分
02. サーブレットからフレームワークへ15分
03. ルーティング表という発想15分
04. プロジェクトの構成10分
05. 起動のしくみと自動設定15分
06. パスのマッチング15分
2
DIコンテナ
01. newで書くと何が困るのか15分
02. コンストラクタインジェクション15分
03. インターフェースへの依存15分
04. @Componentと@Bean15分
05. 自作のDIコンテナ20分
06. Beanのスコープとライフサイクル15分
3
RESTコントローラ
01. RESTという設計方針15分
02. @RestControllerの役割15分
03. @GetMappingとパス変数15分
04. クエリパラメータの受け取り15分
05. @PostMappingとリクエストボディ15分
06. ステータスコードの選び方15分
07. ResponseEntityで組み立てる15分
4
リクエストとレスポンスの変換
01. JSONとオブジェクトの往復15分
02. DTOとエンティティを分ける15分
03. recordでDTOを書く15分
04. 入力検証の基本15分
05. 検証エラーをまとめて返す15分
06. アノテーションによる検証15分
5
レイヤ構成とビジネスロジック
01. 3層アーキテクチャ10分
02. @Serviceに置くもの15分
03. 状態遷移をルールにする15分
04. 料金計算のような業務ロジック15分
05. @Transactionalとは何か15分
06. トランザクションの巻き戻し15分
6
データアクセス
01. @Entityとテーブルの対応15分
02. リポジトリという抽象15分
03. メソッド名からクエリを作る20分
04. 検索条件の組み立て15分
05. ページングと並び替え15分
06. N+1問題15分
7
例外処理と横断的関心事
01. 例外をHTTPに変換する15分
02. @ExceptionHandlerと@ControllerAdvice15分
03. エラーレスポンスの形を決める15分
04. フィルタとインターセプタ15分
05. ログとリクエストの追跡15分
06. 設定値の外部化15分
8
テストと仕上げ
01. テストの種類と使い分け15分
02. Serviceの単体テスト15分
03. モックで依存を差し替える15分
04. MockMvcでエンドポイントを試す15分
05. 本番へ出すための次の一歩10分

エラーレスポンスの形を決める

このレッスンでは、エラー応答の本文をどんな形にそろえるかを決め、そのとおりに組み立てられるようになります。

形がばらばらな API は使えません

同じアプリなのに、ある画面では {"error":"not found"} が返り、別の画面では {"message":"見つかりません"} が返り、さらに別のところでは HTML のエラーページが返ってくる。こうした API を相手にすると、クライアントはエラーごとに読み取りのコードを書き分けることになります。エラーの本文は、成功時の本文よりもむしろ形をそろえる価値が高い部分です。成功のときは呼び出し側が何を期待しているか分かっていますが、失敗はいつどこで起きるか分からないため、共通の読み方が必要になるからです。

そろえるべき項目は次の通りです。

項目役割
ステータスHTTP のステータス行と同じ数字。本文だけを見ても状況が分かる
エラーコードプログラムが分岐に使う短い識別子。文言が変わっても壊れない
メッセージ人が読むための説明。開発者や運用者が原因を掴むためのもの

エラーコードとメッセージを分けるのが要点です。メッセージは日本語にしたり文面を練り直したりと頻繁に変わりますが、クライアントの分岐がメッセージの文字列に依存していると、文面を直した瞬間に相手のアプリが壊れます。USER_NOT_FOUND のような不変のコードを別に持たせておけば、文面は自由に直せます。

Problem Details という共通の答え

この「エラー本文をどうそろえるか」という問いには、RFC 9457 の Problem Details という標準の答えがあります。以前の RFC 7807 を置き換えたもので、application/problem+json という媒体型で、決まった名前の項目を持つ JSON を返す取り決めです。よく使われる項目は次の通りです。

  • type — 問題の種類を表す URI。同じ種類のエラーには同じ URI を使う
  • title — 種類を短く表した、人が読む見出し
  • status — HTTP ステータスコード
  • detail — この 1 回の発生に固有の説明
  • instance — この 1 回の発生を指す URI

JSON

{ "type": "https://example.com/probs/out-of-credit", "title": "You do not have enough credit.", "status": 403, "detail": "Your current balance is 30, but that costs 50." }

title は種類ごとに固定、detail は発生ごとに変わる、という役割分担が肝心です。この区別が守られていると、クライアントは type と title で分岐し、detail は画面に出すだけ、という素直な扱いができます。Spring Framework 6 以降には ProblemDetail という型が用意されているので、独自の形を発明する前に、まずこの標準に寄せられないかを検討してください。

本文に載せてはいけないもの

スタックトレース、SQL 文、内部のクラス名、環境変数の値は、エラー本文に載せてはいけません。攻撃者にとっては構成を推測する材料になります。原因の追跡に必要な情報はサーバーのログへ書き、本文には次のレッスンで扱うトレース識別子だけを載せます。利用者から「エラーになりました」と連絡が来たとき、その識別子でログを引ければ十分です。

演習で書くもの

Spring では @RestControllerAdvice のハンドラが返したオブジェクトを、Jackson が JSON へ変換します。ここではその組み立てを自分で書きます。文字列として JSON を作る以上、本文に引用符や円記号が含まれていたときに壊れないよう、自分でエスケープする必要があります。Jackson が黙ってやってくれている仕事を、1 度だけ手で書いてみる回です。

要件

  1. 項目の順を status、code、message に固定し、空白を入れずに組み立てること
  2. message に含まれる引用符と円記号と改行を JSON のエスケープ記法へ変換すること
  3. code が null なら UNKNOWN、message が null なら空文字として扱うこと

入出力例

buildErrorBody(404, "USER_NOT_FOUND", "user 7 was not found") → "{"status":404,"code":"USER_NOT_FOUND","message":"user 7 was not found"}" buildErrorBody(400, "VALIDATION_FAILED", "name is required") → "{"status":400,"code":"VALIDATION_FAILED","message":"name is required"}" buildErrorBody(500, "INTERNAL_ERROR", "unexpected "boom" happened") → "{"status":500,"code":"INTERNAL_ERROR","message":"unexpected \"boom\" happened"}" buildErrorBody(409, "DUPLICATE", "path C:\temp is taken") → "{"status":409,"code":"DUPLICATE","message":"path C:\\temp is taken"}" buildErrorBody(400, "BAD_REQUEST", "line1 line2") → "{"status":400,"code":"BAD_REQUEST","message":"line1\nline2"}" buildErrorBody(403, "FORBIDDEN", "") → "{"status":403,"code":"FORBIDDEN","message":""}"

ヒント

status は数値なので引用符で囲みません。code と message は囲みます

エスケープは 1 文字ずつ調べる方法が確実です。StringBuilder に足していきます

Java のソース上では、引用符 1 文字は 2 文字、円記号 1 文字は 2 文字で書く点に注意してください

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/09

関連レッスン

  • フィルタとインターセプタ

    連なった処理を順に通すチェーンを実装します。

  • ログとリクエストの追跡

    リクエストを識別する ID を組み立て、追跡できるようにします。

  • 設定値の外部化

    設定ファイルの key=value を解析し、既定値を適用します。

  • テストの種類と使い分け

    @SpringBootTest と @WebMvcTest の違いを確認します。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • エンドポイント「この URL を叩くとこの機能が動く」入口の住所
  • APIアプリ間でデータをやり取りする窓口
  • HTMLHyperText Markup Languageの略。
  • HTTPWeb の通信プロトコル、HTTPS は TLS で暗号化したもの
  • applicationJSON 本文を送るときの Content-Type
  • ステータスコード200/201/404 など結果を示す3桁の数値
  • スタック後入れ先出し(LIFO)のデータ構造
  • SQLデータベースを操作するための共通言語
main.java
学習モード
エディタを読み込んでいます

メモ

エラーレスポンスの形を決める

⌘S で保存