9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Spring Boot で REST API のエラー時のレスポンスをカスタマイズする(後編)

Last updated at Posted at 2019-03-25

概要

Spring Boot でエラー時のレスポンスをカスタマイズしてみたので備忘録としてここに記載します。
前回の記事 では、独自例外を定義し、その例外をハンドルするようにしましたが、今回は Spring が独自に定義している例外のハンドルについて記載したいと思います。

経緯

前回の記事で定義した独自の例外を補足できたのですが、しかし、 MethodArgumentNotValidException 例外初め Spring が定義している例外が発生した際、そのエラー内容がボディ部が null で返ってきました。今回は、その原因と対応方針をまとめてみます。

前提条件

環境

  • SpringBoot 2.1.3
  • Java 1.8.0_181

原因

「Spring 徹底入門 Spring Framework による Java アプリケーション開発」には下記のように記載があります。
以下、引用をさせて頂きます。

ResponseEntityExceptionHandler には、Spring MVC のフレームワーク処理で発生する例外をハンドリングする @ExceptionHandler メソッドが実装されています。つまり、上記のようなクラスを作成するだけで、フレームワーク処理で発生する例外を全てハンドリングすることができます。ResponseEntityExceptionHandler クラスを継承したクラスを作成しただけだと、レスポンスボディはからの状態でエラー応答されます。
ResponseEntityExceptionHandler を継承したクラスを作成しただけだと、レスポンスボディは空の状態で応答されます。レスポンスボディにエラー情報を出力する場合は、 handleExceptionInternal メソッドをオーバーライドします。

直接的な原因はこれで分かりました。
前回の記事 では独自例外に対するハンドル処理は実装していたのですが、 Spring が定義している例外については一切ハンドル処理を定義していなかったため、例外が発生したとしてもボディ部がブランク表示されてしまったのだと思います。
( = ResponseEntityExceptionHandler を継承したハンドルクラスを定義した段階で、独自例外だけではなく、他の例外が発生した時もそのハンドル方法を定義しないといけないらしいです。この辺り、あまり詳しくないのですので、詳しい方教えてください。。。)

今度は、それをコードベースで確認をしてみました。

親クラスである ResponseEntityExceptionHadler クラスの MethodArgumentNotValidException をハンドルする handleMethodArgumentNotValid メソッドを確認すると、ボディ部に明示的に null が渡されているし、デバックをするとこの処理を通っています。

ResponseEntityExceptionHadler.java
protected ResponseEntity<Object> handleMethodArgumentNotValid(
			MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

	return handleExceptionInternal(ex, null, headers, status, request);
}

handleExceptionInternal の引数の内容や順番に関しては、 前回の記事 を参照ください。

対応方針

上記から分かるようにメソッドの引数のボディ部に明示的に null を渡しているので、このメソッドをオーバーライドして、ボディ部に実際に表示させたいエラー内容を渡してあげれば表示されるはずです。
では、実際に動かしてみたいと思います。

コントローラの定義

HelloSpringExceptionController.java
package com.example.demo.springExceptionErrorResponse;

import com.example.demo.errorResponse.ErrorDetail;
import com.example.demo.errorResponse.MyException;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/springexceptionhandle")
public class HelloSpringExceptionController {

    @RequestMapping("/test")
    public void get(@RequestBody @Validated Person person, Model model) {
    }
}

モデルクラスの定義

Person.java
package com.example.demo.springExceptionErrorResponse;

import lombok.Data;

import javax.validation.constraints.NotNull;

@Data
public class Person {
    @NotNull
    String firstName;
    @NotNull
    String lastName;
}

例外ハンドラクラスの定義

今回は簡単に "エラーだよ。" という文字列を渡してみました。
ここに関してはエラー時のボディ用のクラス定義を行なうなどして、よしなに定義してみてください。

SpringExceptionHandler.java
package com.example.demo.springExceptionErrorResponse;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@RestControllerAdvice
public class SpringExceptionHandler extends ResponseEntityExceptionHandler {
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(
            Exception ex,
            Object object,
            HttpHeaders headers,
            HttpStatus status,
            WebRequest request) {
        return super.handleExceptionInternal(ex, "エラーだよ。", headers, status, request);
    }
}

実行

下記、 JSON を http://localhost:8080/springexceptionhandle/test に対して POST でリクエストしてみます。
Person.java には firstName に対して @NotNull 制約が適応されているので、このリクエストはエラーになるはずです。

input.json
{
  "firstName": null,
  "lastName": "名前"
}

結果

エラーだよ。

上記はレスポンスボディとしては寂しいですが、エラー発生時時刻、ステータスコードなどを表示させたい場合には、前回の記事 のようにレスポンスボディ専用のクラスを定義して super.handleExceptionInternal メソッドの2つ目の引数にそのクラスのインスタンスを渡してあげれば表示されるはずです。

関連記事

Spring Boot で REST API のエラー時のレスポンスをカスタマイズする(前編)

参考記事

今回、記事を記載する上で下記の記事を参考にさせて頂きました。

Spring 徹底入門 Spring Framework による Java アプリケーション開発
Spring Bootで作成したREST APIのエラーレスポンスをカスタムする
SpringBootでエラーハンドリング
SpringBootの@RestControllerで例外処理をする

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?