【Spring】Exception Handlerで例外処理を行う(サンプルコードあり)

【Spring】Exception Handlerで例外処理を行う (サンプルコードあり) バックエンド
スポンサーリンク

はじめに

この記事で分かること
  • Exception Handlerの概要
  • SpringでのException Handlerを使用した例外処理方法
ぴんくうさぎ
ぴんくうさぎ

Exception Handlerを作るタスクがうさぎに与えられた!
まず、Exception Handlerってなんだ?

みどりがめ
みどりがめ

Exception Handlerは一言でいうと、「アプリケーション内のエラーの監視役」だよ。

アプリケーション内で指定した例外が発生した時に決まった操作をしたいときに使用するよ。

Exception Handlerを定義することで、アプリケーション全体のエラーを見張ってくれるので、

何度も同じ処理を書く必要がなくなるよ!

サンプルコードと解説

初めに本記事で使用するサンプルコードを掲載します!
あくまでException Handlerの説明がテーマなので、説明に必要なControllerクラス、Serviceクラス、Exceptionクラス、Exception Handlerクラスのみとさせていただいております。

今回は、コントーラー、サービス内でNotFoundException(404:NOT FOUNDを想定)が生じた際のレスポンスの情報を詰めて返す役割をException Handlerに任せます。

Exceptionクラス

public class NotFoundException extends RuntimeException {

    public NotFoundException(Integer id) {
        super("id:" + id + " のトピックが見つかりませんでした。");
        this.id = id;
    }

    private Integer id;
    
}

今回扱うExceptionクラスです。指定されたidがDBに無い時にThrowすることを想定しています。
RuntimeExceptionを継承したクラスを作成し、引数のidを含んだメッセージを渡しています。

Exception Handlerクラス

@RestControllerAdvice //・・・①
public class ExceptionHundler {

    @ExceptionHandler(NotFoundException.class) //・・・②
    public ResponseEntity<ResourceError> handleNotFoundException(NotFoundException e){
        var error = new ResourceError();
        error.setStatus(HttpStatus.NOT_FOUND.toString());
        error.setMessage(e.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }
}

①RestControllerAdviceのアノテーションを付与すると、アプリケーション内の全てのコントローラーを共通的にハンドリングすることができます。また、下記のように記述することで、特定のコントローラーのみハンドリングすることも可能です。

@RestControllerAdvice(assignableTypes = {ApiSampleController.class})

②ExceptionHandler(NotFoundException.class) のアノテーションを付与することで、アプリケーション内で、NotFoundExceptionがThrowされた時にアノテーションが付与されたメソッドが呼ばれます。

Controllerクラス

@RestController
@RequestMapping("/api-sample")
public class ApiSampleController {

    @Autowired
    ApiSampleService apiSampleService;

    @GetMapping("/{id}")
    public ResponseEntity<ApiSampleModel> searchTopicById(@PathVariable Integer id){
        ApiSampleModel model=  apiSampleService.findById(id);
        return ResponseEntity.ok(model);
    }

今回、使用するControllerです。特段、本記事のポイントとなる箇所はありません。
パスパラメータで受け取ったidをもとに主キー検索を行う処理です。

Serviceクラス

    private final ApiSampleRepository apiSampleRepository;

    public ApiSampleModel findById(Integer id){
        ApiSampleModel model = apiSampleRepository.findById(id);
        if(null == model){
            throw new NotFoundException(id);
        }
        return model;
    }

引数で受け取ったidをもとにリポジトリ(DB)から検索処理を行います。
データが取得できない場合に、NotFoundExceptionをThrowしています。これをException Handlerが検知します。

(補足)バリデーションエラー時のExceptionを追加

バリデーションエラー(400 BAD REQUEST)を想定したException Handlerを上記コードに追加しました。追加部分を以下に記載します。

Exceptionクラス

    public BatRequestException(ApiSampleForm form) {
        super(String.format("タイトルまたは内容が不正です。(title=%s,content=%s)",form.getTitle(),form.getContent()));
        this.title = form.getTitle();
        this.content = form.getContent();
    }

    /**
     * タイトル
     */
    private String title;

    /**
     * 内容
     */
    private String content;
}

Exception Handlerクラス

    @ExceptionHandler(BatRequestException.class)
    public  ResponseEntity<ResourceError> handleBatRequestException(BatRequestException e){
        var error = new ResourceError();
        error.setStatus(HttpStatus.BAD_REQUEST.toString());
        error.setMessage(e.getMessage());
        return  ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
    }

Controllerクラス

    @PostMapping("/createTopic")
    public ResponseEntity<ApiSampleModel> createTopic(@RequestBody ApiSampleForm form){
        ApiSampleModel model = apiSampleService.addTopic(form);
        return  ResponseEntity.status(HttpStatus.CREATED).body(model);
    }

Serviceクラス

    public ApiSampleModel addTopic(ApiSampleForm form){
        if(formCheck(form)){
            throw new BatRequestException(form);
        }
        ApiSampleModel model = new ApiSampleModel(form.getTitle(), form.getContent());
        apiSampleRepository.insertTopic(model);
        return model;
    }

参考

今回使用しているコードは下記で詳細を解説しています。
もしよろしければ併せてご覧ください。

終わりに

最後にSpring学習のおすすめ教材を紹介します。
多くの方に支持されている人気商品です。気になる方は是非一度、覗いてみてください。

本記事はここまでとなります。続きは第2編に記載しておりますので是非、併せて参照ください!

ご覧いただきありがとうございました。ご指摘等がございましたら頂けますと嬉しいです。
引き続き、プログラミングについて定期的に発信していきますのでよろしくお願いします!
また、もしよろしければtwitterもフォローしていただけると嬉しいです!🐢

コメント