概要
REST APIアプリにて、入力チェックを行った際のエラーハンドリング方法についてまとめた。
エラーメッセージはBean Validationのデフォルトメッセージを使用する。
概要 REST APIアプリにて、入力チェックを行う方法についてまとめた。 尚、入力チェックエラーハンドリング方法は扱わない。
前提
動作確認はTalend API Testerを使用した。
Talend API Testerの使用方法については以下を参照。
概要 GUIのツールを使用してシンプルにREAT APIアプリにリクエストを送りたいと思い、Talend API Testerを使用してみた。 いろいろと便利だったため、基本的な使用方法について簡単にまとめた。 事[…]
基本的な入力チェックエラーハンドリング
ResponseEntityExceptionHandlerを継承したアドバイスクラスを作成し、スローされる例外にあわせたメソッドをオーバーライドする。
入力チェックエラーにより、スローされる例外は以下を想定している。
・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;
}
}
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
クラスレベルに@ControllerAdviceを付与する。
ResponseEntityExceptionHandlerを継承したクラスを作成する。
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
MethodArgumentNotValidExceptionをハンドリングしてカスタマイズしたい場合、handleMethodArgumentNotValidをオーバーライドする。
上記例外がスローされたら、このメソッドが実行される。
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatus status,
BindExceptionをハンドリングしてカスタマイズしたい場合、handleBindExceptionをオーバーライドする。
上記例外がスローされたら、このメソッドが実行される。
MethodArgumentNotValidExceptionまたはBindExceptionが発生した場合、HTTPステータスに400(Bad Request)が設定される。
どちらのケースも、バリデーションエラーとなったフィールド名とバリデーションデフォルトメッセージのマップ情報を作成し、レスポンスボディに設定している。
.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を使用することで、どのフィールドでどんなバリデーションエラーとなったのか等の情報を取得できる