【Spring MVC】REST APIにて基本的な入力チェックエラーハンドリングを行う方法

概要

REST APIアプリにて、入力チェックを行った際のエラーハンドリング方法についてまとめた。
エラーメッセージはBean Validationのデフォルトメッセージを使用する。

あわせて読みたい

概要 REST APIアプリにて、入力チェックを行う方法についてまとめた。 尚、入力チェックエラーハンドリング方法は扱わない。

REST APIにて基本的な入力チェックを行う方法

 

前提

動作確認はTalend API Testerを使用した。
Talend API Testerの使用方法については以下を参照。

あわせて読みたい

概要 GUIのツールを使用してシンプルにREAT APIアプリにリクエストを送りたいと思い、Talend API Testerを使用してみた。 いろいろと便利だったため、基本的な使用方法について簡単にまとめた。   事[…]

Talend API Testerを使用してREST APIにリクエストする方法

 

基本的な入力チェックエラーハンドリング

ResponseEntityExceptionHandlerを継承したアドバイスクラスを作成し、スローされる例外にあわせたメソッドをオーバーライドする。

入力チェックエラーにより、スローされる例外は以下を想定している。

・MethodArgumentNotValidException: リクエストボディのBean Validationエラー時など
・BindException: クエリパラメータのBean Validationエラー時など

 

エラーハンドリングクラス

ResponseEntityExceptionHandlerを継承したエラーハンドリングクラスを作成する。
ResponseEntityExceptionHandlerを継承することで、Spring MVCのデフォルトの例外をハンドリングする様々なメソッドをオーバーライドして利用できる。

このクラスに@ControllerAdviceを付与することで、例外がスローされたタイミングで例外をハンドリングでき、適切なResponseEntityを返却できる。

 

CustomExceptionHandler.java


package com.example.rest_prototype.web.advice;

import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

/**
 * 例外共通ハンドリングクラス
 *
 */
@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

	/**
	 * 入力チェックエラー(リクエストボディなど)
	 */
	@Override
	protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
			HttpHeaders headers, HttpStatus status, WebRequest request) {
		// エラー情報取得
		var errors = createInputError(ex.getBindingResult());

		return ResponseEntity.status(status).body(errors);
	}

	/**
	 * 入力チェックエラー(クエリパラメータなど)
	 */
	@Override
	protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status,
			WebRequest request) {
		// エラー情報取得
		var errors = createInputError(ex.getBindingResult());

		return ResponseEntity.status(status).body(errors);
	}

	/**
	 * エラー情報作成
	 * @param rs
	 * @return
	 */
	private Map<String, String> createInputError(BindingResult br) {
		// フィールド名、デフォルトメッセージのマップ情報を返却
		var errors = new HashMap<String, String>();
		br.getFieldErrors()
		.forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));

		return errors;
	}
}

 

@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

クラスレベルに@ControllerAdviceを付与する。
ResponseEntityExceptionHandlerを継承したクラスを作成する。

 

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,

MethodArgumentNotValidExceptionをハンドリングしてカスタマイズしたい場合、handleMethodArgumentNotValidをオーバーライドする。
上記例外がスローされたら、このメソッドが実行される。

 

@Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status,

BindExceptionをハンドリングしてカスタマイズしたい場合、handleBindExceptionをオーバーライドする。
上記例外がスローされたら、このメソッドが実行される。

 

return ResponseEntity.status(status).body(errors);

MethodArgumentNotValidExceptionまたはBindExceptionが発生した場合、HTTPステータスに400(Bad Request)が設定される。
どちらのケースも、バリデーションエラーとなったフィールド名とバリデーションデフォルトメッセージのマップ情報を作成し、レスポンスボディに設定している。

 

br.getFieldErrors()
.forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));

BindingResultのgetFieldErrors()を使用することで、バリデーションエラーとなったすべてのフィールド情報を取得できる。
上記はエラーとなったフィールド情報から、フィールド名とデフォルトエラーメッセージを取得してマップ情報に格納している。

 

動作確認

サーバーを起動して、Talend API Testerなどを使用して動作確認を行う。

 

クエリパラメータのバリデーションエラー

nameフィールドをクエリパラメータに設定しない状態でリクエスト送信する。
バリデーションエラーとなったフィールド名と、デフォルトのバリデーションエラーメッセージが返却される

 

リクエスト


http://localhost:8080/rest_prototype/rest06/?hogeDate=2025-02-01

レスポンス


{"name":"nullは許可されていません"}

 

リクエストボディのバリデーションエラー

nameフィールドを設定しない状態でリクエスト送信する。
バリデーションエラーとなったフィールド名と、デフォルトのバリデーションエラーメッセージが返却される。

 

リクエスト


http://localhost:8080/rest_prototype/rest06/create

リクエストボディ


{"id": "4","hogeDate": "2025-01-10"}

レスポンス


{"name":"nullは許可されていません"}

 

まとめ

 

☑ ResponseEntityExceptionHandlerを継承したクラスに@ControllerAdviceを付与することで、例外ハンドリングを一元管理できる

☑ ResponseEntityExceptionHandlerのメソッドをオーバーライドする場合、基本的に戻り値はResponseEntityとなる

☑ BindingResultを使用することで、どのフィールドでどんなバリデーションエラーとなったのか等の情報を取得できる

 

スポンサーリンク