SpringでエラーメッセージをJSON形式で返す方法とは?初心者向けREST API入門
新人
「SpringでREST APIを作るときって、エラーが起きたときもJSONで返す必要があるんですか?」
先輩
「そうだね。REST APIでは、エラーの内容もJSON形式で返すのが一般的なんだ。」
新人
「なぜJSONでエラーを返す必要があるんですか?HTMLのエラーページじゃダメなんですか?」
先輩
「それはとても大事なポイントだね。APIを使うのは主にフロントエンドや外部サービスだから、人間が見るためのHTMLよりも、機械が処理しやすいJSON形式の方が適しているんだ。」
新人
「なるほど。じゃあ、実際にどうやってJSONでエラーを返すようにすればいいんですか?」
先輩
「それじゃあ、まずはエラーとHTTPステータスコードの関係について見ていこうか。」
1. REST APIではなぜJSON形式のエラーメッセージが必要なのか?
SpringでREST APIを開発する場合、クライアント側(たとえばJavaScriptやモバイルアプリ)は、APIから返ってくるレスポンスを自動的に処理します。そのとき、HTML形式のエラーページだと、解析が難しくなります。
一方で、JSON形式のエラーメッセージは、以下のようにキーと値の構造で情報を持つため、フロントエンドや外部サービスが簡単にエラーの内容を取得し、ユーザーに適切な表示や処理を行うことができます。
{
"status": 404,
"error": "Not Found",
"message": "指定されたリソースが見つかりませんでした。",
"path": "/api/items/999"
}
このようなJSON形式のエラー情報があれば、クライアント側でもロジックを組みやすくなり、UXの向上につながります。
2. HTTPステータスコードとJSONメッセージの関係性
REST APIでは、処理の成否をHTTPステータスコードで伝えるのが基本です。たとえば、リクエストが成功すれば200番台、エラーなら400番台や500番台を使います。Springでは、例外が発生したときにこれらのステータスコードをカスタマイズしつつ、JSONで詳細情報を返すように設定できます。
Springの@Controllerを使ってREST APIを実装している場合、次のように@ExceptionHandlerで例外をキャッチし、@ResponseBodyを使ってJSON形式でエラー情報を返せます。
@Controller
public class ItemController {
@GetMapping("/api/items/{id}")
@ResponseBody
public Item getItem(@PathVariable("id") Long id) {
if (id == 999) {
throw new ItemNotFoundException("指定されたIDのアイテムは存在しません");
}
return new Item(id, "テストアイテム");
}
@ExceptionHandler(ItemNotFoundException.class)
@ResponseBody
@ResponseStatus(HttpStatus.NOT_FOUND)
public Map<String, Object> handleItemNotFound(ItemNotFoundException ex, HttpServletRequest request) {
Map<String, Object> error = new LinkedHashMap<>();
error.put("status", 404);
error.put("error", "Not Found");
error.put("message", ex.getMessage());
error.put("path", request.getRequestURI());
return error;
}
}
このようにすると、ItemNotFoundExceptionが発生したときに、HTTPステータスコード404とともに、JSON形式で詳細なエラー情報が返されます。
{
"status": 404,
"error": "Not Found",
"message": "指定されたIDのアイテムは存在しません",
"path": "/api/items/999"
}
この方法はSpringのエラーハンドリングの基本であり、初心者がREST APIを作る際にも必ず理解しておきたいポイントです。特にPleiades+Gradle環境でSpringアプリケーションを構築している場合、この設計を採用すれば、外部APIとしても安定した動作が期待できます。
3. ResponseEntityでエラーメッセージをJSONで返す基本的な方法
SpringでREST APIを作成する際に、@ExceptionHandlerを使わずにJSON形式でエラーメッセージを返したい場合は、ResponseEntityを活用する方法があります。ResponseEntityは、HTTPステータスコードやレスポンスヘッダー、ボディの内容を自由に設定できるクラスです。
たとえば、ユーザーの入力が不正だった場合に、400(Bad Request)というHTTPステータスを指定して、エラーメッセージをJSONで返すような実装が可能です。
@Controller
public class LoginController {
@PostMapping("/api/login")
@ResponseBody
public ResponseEntity<Map<String, Object>> login(@RequestParam("username") String username,
@RequestParam("password") String password) {
if (username == null || username.isEmpty()) {
Map<String, Object> error = new LinkedHashMap<>();
error.put("status", 400);
error.put("error", "Bad Request");
error.put("message", "ユーザー名が入力されていません");
error.put("path", "/api/login");
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
// 認証処理(省略)
return ResponseEntity.ok().build();
}
}
このように、ResponseEntityを使えば、HTTPステータスコードと一緒にJSON形式のデータをクライアントへ返すことができます。@ResponseBodyを併用することで、Springがオブジェクトを自動的にJSONに変換してくれます。
4. エラー内容をオブジェクトにまとめて返す方法(独自エラーレスポンスクラスの作成)
より可読性の高いコードにしたい場合は、エラーの内容をオブジェクトとして設計し、そのオブジェクトを返却するスタイルがおすすめです。たとえば、「エラーレスポンス用のクラス」を作成しておけば、毎回Mapを使う必要がなくなります。
まず、以下のようなErrorResponseクラスを定義します。
public class ErrorResponse {
private int status;
private String error;
private String message;
private String path;
public ErrorResponse(int status, String error, String message, String path) {
this.status = status;
this.error = error;
this.message = message;
this.path = path;
}
// getterとsetter(省略してもJSON出力には影響しないが、IDEによっては必要)
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public String getError() { return error; }
public void setError(String error) { this.error = error; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
}
次に、このErrorResponseクラスを使って、ResponseEntityでエラー情報を返却します。
@Controller
public class UserController {
@GetMapping("/api/users/{id}")
@ResponseBody
public ResponseEntity<ErrorResponse> getUser(@PathVariable("id") Long id, HttpServletRequest request) {
if (id <= 0) {
ErrorResponse error = new ErrorResponse(
400,
"Bad Request",
"IDは1以上で指定してください",
request.getRequestURI()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
// 正常処理(省略)
return ResponseEntity.ok().build();
}
}
このように独自のエラーレスポンスクラスを定義しておくことで、エラー内容を統一された形式で返すことができ、フロントエンドとの連携もスムーズになります。@Controllerと@ResponseBodyを組み合わせることで、Springが自動的にJSONへと変換してくれます。
5. よくあるエラー応答の実装例
最後に、Springでよくあるエラーハンドリングの実装例を紹介します。以下は、リクエストパラメータが欠けていた場合や、バリデーションに失敗した場合に、ResponseEntityでJSON形式のエラーを返す基本的なサンプルです。
@Controller
public class FormController {
@PostMapping("/api/form")
@ResponseBody
public ResponseEntity<ErrorResponse> submitForm(@RequestParam(value = "name", required = false) String name,
HttpServletRequest request) {
if (name == null || name.trim().isEmpty()) {
ErrorResponse error = new ErrorResponse(
400,
"Bad Request",
"名前は必須項目です",
request.getRequestURI()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
// 正常処理(省略)
return ResponseEntity.ok().build();
}
}
このように、Springでは@ControllerとResponseEntityを使うことで、例外処理だけでなく、通常の処理の中でも柔軟にJSON形式でエラーメッセージを返すことができます。REST APIの品質を高めるためにも、明確で統一されたエラーレスポンス設計は非常に重要です。
6. @ExceptionHandlerで共通のエラーハンドリングを設計する方法
Springでは、@ExceptionHandlerアノテーションを使うことで、例外を共通の方法で処理し、エラーメッセージをJSON形式で返すことができます。これは、複数のコントローラクラスで同じような例外処理を繰り返すのを避けるための便利な仕組みです。
まずは、独自の例外クラスを定義してみましょう。
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}
次に、例外発生時のレスポンスをまとめて処理するためのErrorResponseクラスを再利用します。そして、共通のエラーハンドラーを@ControllerAdviceで定義します。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(
400,
"Bad Request",
ex.getMessage(),
request.getRequestURI()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}
@ControllerAdviceは全ての@Controllerに適用されるため、特定の例外が発生した場合に自動的にこのハンドラーが呼び出されます。@ResponseBodyを付けることで、返却されるErrorResponseがJSON形式でクライアントに送られます。
実際のコントローラでは、以下のようにBusinessExceptionを投げるだけで、共通処理に引き渡されます。
@Controller
public class OrderController {
@GetMapping("/api/orders/{id}")
@ResponseBody
public String getOrder(@PathVariable("id") Long id) {
if (id <= 0) {
throw new BusinessException("注文IDは1以上で指定してください");
}
return "注文データ";
}
}
このように設計しておけば、今後同じようなエラー処理が必要になった場合でも、すべてのControllerで一貫性のあるエラーハンドリングが実現できます。
7. 実際のプロジェクトでよく使われるエラーJSONの構造とは?
実際のSpringプロジェクトでは、エラー情報をわかりやすく整理し、クライアントが処理しやすいように工夫されたJSON形式が使われます。以下は、現場でよく使われる代表的な構造です。
{
"status": 400,
"error": "Bad Request",
"message": "入力値に誤りがあります",
"path": "/api/register",
"timestamp": "2025-09-08T18:32:10"
}
ここで注目すべきは、timestampというフィールドです。これは、いつエラーが発生したかを示しており、ログの追跡やバグ調査に役立ちます。Springの中では、LocalDateTimeなどを使ってこの情報を付与できます。
先ほど定義したErrorResponseクラスにtimestampを追加してみましょう。
public class ErrorResponse {
private int status;
private String error;
private String message;
private String path;
private LocalDateTime timestamp;
public ErrorResponse(int status, String error, String message, String path) {
this.status = status;
this.error = error;
this.message = message;
this.path = path;
this.timestamp = LocalDateTime.now();
}
// getterとsetterは省略
}
このようにすれば、エラーが発生した時刻が自動で記録され、クライアントでもサーバーでも時系列で問題を把握しやすくなります。REST APIの信頼性を高めるためにも、このような拡張はおすすめです。
8. SpringアプリケーションでJSONエラー処理を設計する際の注意点
SpringでエラーメッセージをJSON形式で返す設計を行う際には、いくつかの重要なポイントがあります。初心者がつまずきやすい部分も多いため、以下の点に注意してください。
- 例外処理をすべて
@ExceptionHandlerに任せると、原因の特定が難しくなる場合があります。ログ出力も併用しましょう。 @ControllerAdviceは複数定義できますが、競合や優先度の調整に注意が必要です。- チェック例外(
Exception)を一括キャッチする場合は、意図しない例外を隠さないように十分注意してください。 - 例外を
RuntimeExceptionでくくると開発は楽ですが、事前に妥当性チェックできるものはリクエスト前に防ぐのが理想です。 @ResponseBodyを忘れると、エラーがHTMLで返ってしまうため注意が必要です。
また、Springアプリケーションでは、開発環境がPleiades+Gradleである場合、依存関係の設定を明確にし、Jacksonライブラリなどがプロジェクトに正しく追加されていることを確認してください。JSONへの変換にはObjectMapperではなく、Springが自動で内部的に処理してくれます。
REST APIの信頼性を高めるためには、エラーハンドリングの設計も欠かせません。ユーザーにとって分かりやすく、開発者にとってもデバッグしやすいエラー構造を意識することが、長期的な品質向上につながります。