質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Thymeleaf

Thymeleaf(タイムリーフ)とは、Java用のテンプレートエンジンで、特定のフレームワークに依存せず使用することが可能です。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

1回答

4455閲覧

springbootでの更新時バリデーション実装

kissy

総合スコア17

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Thymeleaf

Thymeleaf(タイムリーフ)とは、Java用のテンプレートエンジンで、特定のフレームワークに依存せず使用することが可能です。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

0クリップ

投稿2018/10/02 08:27

編集2018/10/06 06:51

前提・実現したいこと

springbootでwebアプリケーションの勉強中です。
あらかじめフォームで送った内容を編集・更新する機能を実装しているのですが、エラーが出て困っています。
編集・更新する際に、null禁止などのバリデーションをかけているのですが、バリデーションに引っかかった場合は、元の編集画面に戻し、エラーメッセージを出したいです。
現在、編集画面でフォームを編集、バリデーションに引っ掛かった場合、更新ボタンを押すとエラーが出てしまいます。
バリデーションに引っかからない場合は問題なく編集ができ、ページ遷移します。

発生している問題・エラーメッセージ

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/todo/edit.html]")
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "todo.id" (template: "todo/edit" - line 15, col 11)

該当のソースコード

Controller

1@GetMapping("todo/{id}/edit") 2 public ModelAndView edit(@PathVariable("id") Long id, ModelAndView mav){ 3 4 Optional<Todo> todoOne = todoService.findById(id); 5 if(todoOne.isPresent()){ 6 Todo todo = todoOne.get(); 7 mav.addObject("todo",todo); 8 mav.setViewName("todo/edit"); 9 }else { 10 mav.addObject("error","該当するToDoはありません"); 11 mav.setViewName("todo/edit"); 12 } 13 return mav; 14 } 15 16@PostMapping("/todo/{id}/edit") 17 public ModelAndView updateTodo(@ModelAttribute @Validated TodoValid todoValid, BindingResult result, @PathVariable("id") Long id, RedirectAttributes redirectAttributes,ModelAndView mav){ 18 Todo todo = todoService.getById(id); 19 20 if (result.hasErrors()) { 21 mav.setViewName("todo/edit"); 22 return mav; 23 } 24 25(中略) 26 27 BeanUtils.copyProperties(todoValid, todo); 28 todoService.save(todo); 29 mav.setViewName("redirect:/"); 30 return mav; 31 }

バリデーション設定↓

TodoValid

1package com.example.web.controller; 2 3import org.hibernate.validator.constraints.Length; 4 5import org.springframework.format.annotation.DateTimeFormat; 6import javax.validation.constraints.NotNull; 7 8import java.util.Date; 9 10public class TodoValid { 11 12 @Length(max = 30,message = "Todoは30字以内で入力してください") 13 @NotNull(message = "Todoを入力してください") 14 private String text; 15 16 @NotNull(message = "期限を設定してください") 17 @DateTimeFormat(pattern = "yyyy-MM-dd") 18 private Date limittime; 19 20 public String getText() { 21 return text; 22 } 23 24 public void setText(String text) { 25 this.text = text; 26 } 27 28 public Date getLimittime() { 29 return limittime; 30 } 31 32 public void setLimittime(Date limittime) { 33 this.limittime = limittime; 34 } 35 36 37} 38

エンティティ

Todo

1@Entity 2@Table(name = "todo") 3public class Todo { 4 5 @Id 6 @GeneratedValue(strategy = GenerationType.AUTO) 7 private Long id; 8 9 private String text; 10 11 @DateTimeFormat(pattern = "yyyy-MM-dd") 12 private Date limittime; 13 14 @DateTimeFormat(pattern = "yyyy-MM-dd") 15 @Column(updatable = false) 16 private Date createtime; 17 18 private boolean complete; 19 20(ゲッター、セッター省略)

html

1<!DOCTYPE html> 2<html xmlns:th="http://www.thymeleaf.org"> 3<head> 4 <meta charset="UTF-8" /> 5 <link rel="stylesheet" type="text/css" href="/css/style.css" th:href="@{/css/style.css}" /> 6 <title>Todo編集</title> 7</head> 8<body> 9<header> 10 <a class="logo" th:href="@{'/'}">ToDoリスト管理</a> 11 <div class="serch"><a th:href="@{'/todo' + '/find'}">検索</a></div> 12</header> 13<div class="new"> 14 <p>ToDoの内容を変更します</p> 15 <form th:action="@{'/todo/' + ${todo.id} + '/edit'}" method="post" th:object="{todoValid}"> 16 17 <div th:object="${todo}"> 18 <div>ToDo名 19 <textarea placeholder="すること" name="text" th:text="*{text}" maxlength="30"></textarea> 20 </div> 21 <div>期限 22 <input name="limittime" type="date" th:value="*{#dates.format(createtime,'yyyy-MM-dd')}"/> 23 </div> 24 25 <input class="button" type="submit" value="ToDoの更新" name="new" /> 26 27 </div> 28 29 <div th:each="todoValid:${todoValid}"> 30 <div th:if="${#fields.hasErrors('text')}" th:errors="*{text}"></div> 31 <div th:if="${#fields.hasErrors('limittime')}" th:errors="*{limittime}"></div> 32 </div> 33 34 <div th:if="${error}" th:text="${error}"></div> 35 36 </form> 37 38</div> 39 40</body> 41</html>

###その他
エラーが出ている箇所は「edit.hrml」の

<form th:action="@{'/todo/' + ${todo.id} + '/edit'}" method="post" th:object="${todoValid}"> の「th:action~」部分のようです。 この記述を変更してしまうと、フォームが上手く送れないと思うのですが…

問題の箇所がわかる方がいらっしゃったら、ご教授いただきたいです。

補足情報(FW/ツールのバージョンなど)

・Springboot(gradle)
・thymeleaf
MySQL@5.7
・intelliJ

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/10/04 09:24

><form th:action="@{'/todo/' + ${todo.id} + '/edit'}" method="post" th:object="${todoValid}"> の「th:action~」部分のようです。⇒エラーの線マーク出てるからですかね?   ちなみにformの閉じタグの前の、divの閉じタグが余計な気がするのですがどうでしょう。
kissy

2018/10/04 13:25

ありがとうございます。エラー線は出ておらず、実行も出来るのですが、いざローカル環境で試してみるとエラー…という感じです。エラーメッセージが、「th:action~」にある「~{todo.id}」の部分を差しているようだったので上手く機能していないのかなと思いました。  form閉じタグ前の</div>は確かに余計でしたので、削除しましたが、特に変化はありませんでした…。
退会済みユーザー

退会済みユーザー

2018/10/05 01:17

ちょっとコードお借りしますね。エラーが出るタイミングっていつですか?
退会済みユーザー

退会済みユーザー

2018/10/05 01:17

手順が知りたいです。
kissy

2018/10/05 08:03

何度もお手数をおかけしています。エラーがでるタイミングとしてはブラウザ(ローカル環境)で、「トップページから個別の編集画面に飛ぶ→フォーム上で編集→更新ボタンをクリック…」という手順です。バリデーションに引っかからない(フォームを空欄にしない)場合は問題なく送信でき、データが編集できているのですが、バリデーションに引っ掛かった時にエラーが出てしまう状態です。
退会済みユーザー

退会済みユーザー

2018/10/05 08:07

やってみたのですが・・・正しいのか不明。get時のコードとhtml追記お願いします!
kissy

2018/10/05 09:23

完全に失念していました、すいません。get時のコードなど追記させていただきました。長い間ありがとうございます。
kissy

2018/10/06 07:37

何度もありがとうございました。皆様のお力を借りて、無事解決いたしました。
guest

回答1

0

ベストアンサー

ざっとコードを見て以下の原因ではないかと考えられます。

いつエラーが発生したのか、について言及されていませんが
恐らく初回表示の時点で既にエラーが発生しているのではないでしょうか?

原因は恐らく「${todo.id}参照する際に todo なんてオブジェクトが存在していないため」です。

${todo.id}をFormのアクションURLに利用するために参照されていますが、
todoオブジェクトがModelAndViewに設定されていない経路があります。
具体的には以下のコントローラーの処理です。

GET

java

1@GetMapping("todo/{id}/edit") 2 public ModelAndView edit(@PathVariable("id") Long id, ModelAndView mav){ 3 4 Optional<Todo> todoOne = todoService.findById(id); 5 if(todoOne.isPresent()){ 6 Todo todo = todoOne.get(); 7 mav.addObject("todo",todo); 8 mav.setViewName("todo/edit"); 9 }else { 10 // ここ 11 mav.addObject("error","該当するToDoはありません"); 12 mav.setViewName("todo/edit"); 13 } 14 return mav; 15 }

POST

java

1@PostMapping("/todo/{id}/edit") 2 public ModelAndView updateTodo(@ModelAttribute @Validated TodoValid todoValid, BindingResult result, @PathVariable("id") Long id, RedirectAttributes redirectAttributes,ModelAndView mav){ 3 Todo todo = todoService.getById(id); 4 5 if (result.hasErrors()) { 6 // ここ 7 mav.setViewName("todo/edit"); 8 return mav; 9 }

※全体のソースコードを拝見していないため推測混じりで記載します

edit.hrml参照時は恐らくGetメソッドでアクセスされるかと思いますので
コントローラーの edit メソッドを経由して edit.html を表示しているかと考えられます。
しかし、 id に合致するレコードは存在していないため

java

1if(todoOne.isPresent())

の判定で FALSE となりModelAndViewに対して "error" が設定されます。
ところが、ここで "todo" に対応するオブジェクトを設定してないために
Thymeleaf側で、todo なんてオブジェクトはしらぬ!とエラーになっていると思われます。

試しに以下のURLでアクセスするとエラーは発生しないのではないでしょうか?

todo/レコードが存在するID/edit

逆に以下のURLでアクセスするとエラーが発生するのではないでしょうか?

todo/レコードが存在しないID/edit

解決案(GET版)
簡潔に対応しようとすると、

java

1     if(todoOne.isPresent()){ 2 Todo todo = todoOne.get(); 3 mav.addObject("todo",todo); 4 mav.setViewName("todo/edit"); 5 }else { 6 Todo todo = new Todo(); // add 7 todo.setId(-1); // add 8 mav.addObject("error","該当するToDoはありません"); 9 mav.addObject("todo", todo); 10 mav.setViewName("todo/edit"); 11 }

解決案(POST版)

java

1 if (result.hasErrors()) { 2 Todo todo = new Todo(); 3 todo.setId(-1); 4       /* または既に存在するIDを選択済みであるというのであれば以下のようにサービスから取得したTodoを設定する方が良いかもしれません 5 Todo todo = todoService.findById(id); 6 */ 7 mav.addObject("error","該当するToDoはありません"); 8 mav.addObject("todo", todo); 9 mav.setViewName("todo/edit"); 10 return mav; 11 } 12

長々と失礼しました。
以上、ご確認いただけますか。

投稿2018/10/05 14:39

編集2018/10/05 14:41
Lazy-player

総合スコア77

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kissy

2018/10/06 07:36 編集

ありがとうございます。エラーが出るタイミングをきちんと記載していなくて申し訳なかったのですが、表示までは問題なくできています。その後、文章を編集して更新ボタンを押すと、バリデーションに引っ掛かっている場合、編集画面に戻りエラーメッセージを出したいのに、現状エラーになってしまうということでした。 頂いた解決案のコードを試してみたところ、無事想定通りの動きになりました!{todo.id}の部分が具体的にどう問題なのかいまいちわからなかったのですが、オブジェクトがないから怒られていたのですね。詳しい解説までありがとうございました。
Lazy-player

2018/10/06 08:06 編集

想定通りに動作したという事で承知しました。 記載したコードは”とりあえず動作するようにしたコード"なので適宜ご変更いただければと思います。 具体的な原因については、私の方からは「SpringELの言語仕様で、参照オブジェウトが存在しない場合は例外が発生する」程度の回答しかできず申し訳ありません。 どうぞよろしくお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問