VsCodeでspring boot(ModelAndView,Valid)

VsCodeでspring bootでもう少し開発

ModelAndViewを使用する

前作でModelクラスを使ったものをやりましたが、これをModelAndViewクラスに変えてみましょう。「CTRL+SHIFT+P」でspring initから始めてinitializrを起動してください。

「Spring web web」 「thymeleaf」を追加して、pom.xmlで「webjarsのbootstrap」を追加してください。わからない方はModelを使用するを参照してください

変わるのは「DateController.java」だけで、それ以外はModelの時と同じです。

spmavtest/DateController.java
	package jp.co.etlab.spmavtest;

	import org.springframework.stereotype.Controller;
	import org.springframework.web.bind.annotation.ModelAttribute;
	import org.springframework.web.bind.annotation.RequestMapping;
	import org.springframework.web.bind.annotation.RequestMethod;
	import org.springframework.web.bind.annotation.RequestParam;
	import org.springframework.web.servlet.ModelAndView;
	
	@Controller
	public class DateController {
		@RequestMapping(value="/", method=RequestMethod.GET)
		public ModelAndView index(ModelAndView mav) {
			mav.addObject("message", "日付の計算を行います。日付と差分を入力してください。");
			mav.setViewName("index");
			return mav;
		}
	
		@RequestMapping(value="/result", method=RequestMethod.POST)
		public ModelAndView datecalc(@RequestParam("sdate") String ssdate,
		@RequestParam("dplus") String splusd, ModelAndView mav) {
			DateModel dm = new DateModel();
			dm.setSdate(ssdate);
			dm.setDplus(splusd);
			//計算
			dm.calcDate();
			mav.addObject("message", dm.getSdate() + " の" + Math.abs(dm.getDplus()) +
				"日" + (dm.getDplus()>0 ? "後" : "前") + "は " + dm.getCdate() + "です");
			return mav;
		}
	}

関数の引数が「ModelAndView」に変わりました。またaddAttributeがaddObjectに変わりました。またreturnがオブジェクトになり、戻り先はsetViewNameになりました。これだけです。

mavenでinstall,compile,packageしてください。

起動方法を変えましょう。spring boot extension packを入れたので「spring boot dashboard」のボタンが左の列に出来ています。これを使ってtomcatへの登録と停止ができます。

spring boot dashboard

同じようにブラウザで表示、実行ができます。

起動

停止もdashboardでできます

停止

バリデーションを使う

今度はバリデーションを少し使ってみましょう。実はネットに載っている方法を色々やったのですが、うまくいきませんでした。本も購入してやってみましたが、どうもしっくりきません。なのでとりあえずの方法を出しておきます。

今回やっているのは、Model,ModelAndViewなどをやった実験と同じ流れのなかでやります。今まではsdateもdplusもrequiredにしているので抜けることがなかったので、sdateに関してだけrequiredをあえて外しています。dplusはメッセージに関してどうも納得いかないのでまた後程です。

例によってspring initializrで作製してしてください。今度は「spring Web」 「thymeleaf templates」 「validation」と手入力でbootstrap入れてください。

pom.xmlはこんな風になると思います。

spvalidtest/pom.xml抜粋
	<dependencies>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>bootstrap</artifactId>
			<version>5.3.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

今度はソースが変わります。implementsでWebMvcConfigurerを入れます。registoryの登録が増えています

DateController.java
	package jp.co.etlab.spvalidtest;

	import org.springframework.stereotype.Controller;
	import org.springframework.ui.Model;
	import org.springframework.validation.BindingResult;
	import org.springframework.web.bind.annotation.GetMapping;
	import org.springframework.web.bind.annotation.PostMapping;
	import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
	import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
	
	import jakarta.validation.Valid;
	
	@Controller
	public class DateController implements WebMvcConfigurer{
		@SuppressWarnings("null")
		@Override
		public void addViewControllers(ViewControllerRegistry registory) {
			registory.addViewController("/result").setViewName("result");
		}
	
		@GetMapping("/")
		public String getMethodName(DateModel dateModel) {
			return "index";
		}
		
		@PostMapping("/result")
		public String postMethodName(@Valid DateModel dateModel,BindingResult br, Model model) {
			if (br.hasErrors()) {
				return "index";
			}
			//計算
			dateModel.calcDate();
			model.addAttribute("message", dateModel.getSdate() + " の" + Math.abs(dateModel.getDplus()) +
						"日" + (dateModel.getDplus()>0 ? "後" : "前") + "は " + dateModel.getCdate() + "です");
			return "result";
		}
	}

BindingResultでバリデーションが行われ、エラーがあれば.hasErrors()はtrueになります。その時はindexに戻っていますが、 その時dateModelに宣言したmessageが渡って、th:if="${#fields.hasErrors('sdate')}" th:errors="*{sdate}"でエラーがあれば表示されます。

正常時は、今まで通りです。index.htmlがそのバリデーション関係で変わっています

index.html
	<!doctype html>
	<html lang="ja" xmlns:th="http://www.thymeleaf.org">
	  <head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>ModelAndView Test</title>
		<link th:href="@{/webjars/bootstrap/5.3.0/css/bootstrap.min.css}" rel="stylesheet">
	  </head>
	  <body>
		<div class="container">
		  <main>
			<div class="py-5 text-center">
			  <h2>ModelAndView test</h2>
			  <p class="lead" th:text="${message}"></p>
			</div>
			<div class="text-center">
			  <h4 class="mb-3">日付計算</h4>
			  <form class="needs-validation" th:action="@{/result}" th:object="${dateModel}" method="POST">
				  <div class="row g-12">
					<div class="col-sm-6">
					  <label for="sdate" class="form-label">日付</label>
					  <input type="date" class="form-control" id="sdate" name="sdate" placeholder="yyyy-MM-ddで日付を記述してください" value="" th:field="*{sdate}">
					  <span class="text-danger" th:if="${#fields.hasErrors('sdate')}" th:errors="*{sdate}">Date Error</span>
					</div>
		
					<div class="col-sm-6">
					  <label for="plusd" class="form-label">前後日付</label>
					  <input type="number" class="form-control" id="dplus" name="dplus" placeholder="+ -付きで日数を入力してください" value="" required>
					  <span class="text-danger" th:if="${#fields.hasErrors('dplus')}" th:errors="*{dplus}">Data plus Error</span>
					</div>
				  </div>
		
				  <hr class="my-4">
		
				  <button class="w-100 btn btn-primary btn-lg" type="submit">計算実行</button>
			  </form>
			</div>
		  </main>
		
		  <footer class="my-5 pt-5 text-body-secondary text-center text-small">
			<p class="mb-1">© 2024 Etlab</p>
		  </footer>
		</div>
		<script th:src="@{/webjars/bootstrap/5.3.0/js/bootstrap.min.js}"></script>
	  </body>
	</html>

formでobjectを展開しています。その要素としてのsdateを見て、バリデーション結果を表示しています。