Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@nomad_kartman

JavaでTODOアプリを制作しよう11 存在しないIDのTODOにアクセスした時の例外処理

こんにちは。

Java + SpringでTODOアプリを作るシリーズですが、今回からいよいよ例外処理を実装していきます。

まずは例外処理に慣れるためにも比較的簡単なところから始めたいので、存在しないIDにアクセスした時の例外処理を見てみましょう!

TODOアプリ作成リンク集

1: [超基礎の理解] MVCの簡単な説明
2: [雛形を用意する] Spring Initializrで雛形を作ってHello worldしたい
3: [MySQLとの接続・設定・データの表示] MySQLに仮のデータを保存 -> 全取得 -> topに表示する
4: [POST機能] 投稿機能の実装
5: [PATCH機能] TODOの表示を切り替える
6: [JpaRepositoryの簡単な使い方] 検索機能の実装
7: [Thymeleaf テンプレートフラグメントで共通化] Headerの作成
8: [PUT機能] 編集機能の実装
9: [微調整]TODOの表示を作成日時が新しい順にソートする + 期日のデフォルトを今日の日付にする
10: [springで例外処理] 例外処理についての簡単なまとめ
11: [springで例外処理] 存在しないIDのTODOにアクセスした時の例外処理(今ここ)

例外処理がどう処理されるか考えてみる

さて前回の記事では例外処理とは何なのかについて簡単に説明しました。

要約すると

ユーザーが製作者の意図しないリクエストをした時(TODO登録時に規定数以上の文字を入力する、存在しないURLにアクセスしようとする ...etc)、特定のエラーページへ誘導するすることでセキュリティ面・ユーザビリティの向上を図る。

ということになります。

例外処理の流れ

今回実装するのは存在しないIDのTODOにアクセスをリクエストした際ですので・・・

まずはコントローラー確認

com/example/todo/TodoController.java
    @GetMapping("/edit/{id}")
    public String showEdit(Model model, @PathVariable("id") long id ) {
        TodoEntity editTarget = todoService.findTodoById(id);
        model.addAttribute( "editTarget" , editTarget);
        return "edit";
    }

このshowEdit呼び出し時の例外処理となります。todoService.findTodoById(id)で Serviceクラスを経由してTODOを取得するわけですが、findTodoById()をみてみましょう。

ServiceクラスのfindTodoById()の確認

com/example/todo/TodoService.java
    public TodoEntity findTodoById(Long todoId) {
        Optional<TodoEntity> todoResult = todoRepository.findById(todoId);
        return todoResult.get();
    }

RepositoryからID検索をしてTODOを取得してOptional型で返しています。

しかしここで存在しないIDで検索しようとするとエラーで落ちてしまうので、このタイミングで

もしtodoResultが空だったら特定のExceptionクラスへ誘導する

様にすればいいでしょう!

特定のExceptionクラス?

com/example/todo/exception/TodoNotFoundException.java
package com.example.todo.exception;
public class TodoNotFoundException extends RuntimeException{
}

exceptionというディレクトリを新しく作り、その中にTodoNotFoundExceptionというクラスを作ります。

extendsによってこのクラスはRuntimeExceptionを継承していることになります。
継承したことによってこのクラスでもRunttimeExceptionのメソッドが使用できるようになります。(今回は使用しないですが・・・)

findTodoById()内でtodoResultが空だった時にTodoNotFoundExceptionクラスへ誘導する

com/example/todo/TodoService.java
    public TodoEntity findTodoById(Long todoId) {
        Optional<TodoEntity> todoResult = todoRepository.findById(todoId);
        todoResult.orElseThrow(TodoNotFoundException::new);
        return todoResult.get();
    }

returnの前に追加した1行を見てみましょう!

Optional.orElseThrow(Exception名::new)とすることでOptional型が空だった時に指定したクラスへ処理を飛ばすことが出来ます。(今回は先ほどつくったTodoNotFoundException)

ちなみにException名::newの部分はメソッド参照と呼ばれていて、メソッドの引数内としてメソッドを参照するものとなります。

こちらの記事が参考になるので見てみてください。

TodoNotFoundExceptionを管理するクラスを作る。

先ほど作ったTodoNotFoundExceptionRuntimeExceptionを継承しただけで中身は空でしたね。

com/example/todo/exception/TodoNotFoundException.java
package com.example.todo.exception;
public class TodoNotFoundException extends RuntimeException{
}

ここで@ControllerAdviceというアノテーションを使って、TodoNotFoundExceptionが呼ばれた時(=スローされた時)の処理を実装していきます。

com/example/todo/exception/TodoControllerAdvice.java
package com.example.todo.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

@Slf4j
@ControllerAdvice
public class TodoControllerAdvice {
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(TodoNotFoundException.class)
    public String idNotFound() {
        log.warn("指定されたTODOが見つかりません。");
        return "error/404.html";
    }
}

exceptionディレクトリ内にこんなクラスを作ってみましょう。

上から順に説明すると・・・

@Slf4j
このアノテーションを使う事で、ログを表示できる様になります。log.warn("内容")やlog.info("内容")でターミナルにログを表示できます。

@ControllerAdvice
このクラスがControllerAdviceである事を示します。TodoControllerで例外が発生した時にどのように処理するかを設定できるようになります。

@ResponseStatus(HttpStatus.NOT_FOUND)
Not_FoundとしてHttpStatusを返します。

@ExceptionHandler(TodoNotFoundException.class)
ここが大事なのですが、このアノテーションを用いることによってTodoController内の処理でTodoNotFoundExceptionが発生した際にこのidNotFound()に処理がくるように指定しています。この関数内ではエラーログを出力しつつ、error/404.htmlを表示させるようにしています。

エラーページの作成

templates/error/404.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>404</title>
</head>
<body>
404!
</body>
</html>

本来ならエラー内容を表示させるべきですが、今回は簡素に404!と表示させるだけにします。

今回実装した例外処理の流れまとめ

さて一気に説明したので混乱しているかもしれませんが、今回の処理をまとめると以下になります。

・ユーザーがTodo編集をリクエスト(8080/edit/100)

・コントローラがリクエストに応じてサービスクラスへ処理をまわす(findTodoById(100))

・ID = 100のTODOは存在しないのでTodoNotFoundExceptionが呼ばれる。

@ControllerAdviceがついたTodoControllerAdviceが「指定されたTODOが見つかりません」というログを出すと共に404.htmlへ遷移させる。

こんな感じです!

ちょっと文章だとわかりづらいかもしれないので、こちらの記事を読んでみると良いと思います。

次回も続いて例外処理の実装を進めていきます!

0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
0
Help us understand the problem. What is going on with this article?