大家好,我是方圆
拿上这把军刀,也不要乱砍呐!
package tacos; import lombok.Data; import lombok.RequiredArgsConstructor; @Data @RequiredArgsConstructor public class Ingredient { private final String id; private final String name; private final Type type; public static enum Type{ //包裹物,蛋白质,蔬菜,奶酪,沙司 WRAP,PROTEIN,VEGGIES,CHEESE,SAUCE } }
123456789101112131415161718 @RequiredArgsConstructor:这个时Lombok中的注解,并不常用,它生成的是包含常量和被标记为NotNull变量的构造函数,且为私有构造(private)package tacos; import lombok.Data; import java.util.List; @Data public class Taco { private String name; private List<String> ingredients; } 123456789101112
要满足如下条件:
处理“/design”的 HTTP GET请求构建配料的列表处理请求,传递数据,渲染页面给浏览器package tacos; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import static tacos.Ingredient.*; @Slf4j @Controller @RequestMapping("/design") public class DesignTacoController { @GetMapping public String showDesignForm(Model model){ List<Ingredient> ingredients = Arrays.asList( new Ingredient("FLTO", "Flour Tortilla", Type.WRAP), new Ingredient("COTO", "Corn Tortilla", Type.WRAP), new Ingredient("GRBF", "Ground Beef", Type.PROTEIN), new Ingredient("CARN", "Carnitas", Type.PROTEIN), new Ingredient("TMTO", "Diced Tomatoes", Type.VEGGIES), new Ingredient("LETC", "Lettuce", Type.VEGGIES), new Ingredient("CHED", "Cheddar", Type.CHEESE), new Ingredient("JACK", "Monterrey Jack", Type.CHEESE), new Ingredient("SLSA", "Salsa", Type.SAUCE), new Ingredient("SRCR", "Sour Cream", Type.SAUCE) ); Type[] types = Type.values(); for(Type type : types){ //attributeName and attributeValue model.addAttribute(type.toString().toLowerCase(),filterByType(ingredients,type)); } model.addAttribute("design",new Taco()); return "design"; } //根据配料类型过滤列表 private List<Ingredient> filterByType(List<Ingredient> ingredients,Type type) { //对参数进行过滤,将其中与传入的type相同的拿出来后,再变成List return ingredients.stream().filter(x -> x.getType().equals(type)).collect(Collectors.toList()); } }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051Tips: 实际上,存储到Model中的数据将会复制到Servlet Response的属性中,这样才能在视图中找到它们。
return ingredients.stream().filter(x -> x.getType().equals(type)).collect(Collectors.toList());
最后一行代码值得我们关注一下,其中将ingredients转换为流,对其中的内容进行过滤,筛选出与type一致的内容,再转换为List,实现了挑选的功能。
@Slf4j:该注解是Simple Logging Facade for Java的缩写,来自Lombok包,用代码显示如下,即创建一个简单的日志
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DesignTacoController.class); 12
@RequestMapping(method = RequestMethod.GET) 1
虽然可以实现转化,但是GetMapping更加简洁,并且直接表明了它要处理GET请求。
其他请求如下
注解请求PostMapping处理Post请求PutMapping处理Put请求DeleteMapping处理Delete请求PatchMapping处理Patch请求<!-- tag::all[] --> <!-- tag::head[] --> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Taco Cloud</title> <link rel="stylesheet" th:href="@{/styles.css}" /> </head> <body> <h1>Design your taco!</h1> <img th:src="@{/images/TacoCloud.png}"/> <!-- tag::formTag[] --> <form method="POST" th:object="${design}"> <!-- end::all[] --> <span class="validationError" th:if="${#fields.hasErrors('ingredients')}" th:errors="*{ingredients}">Ingredient Error</span> <!-- tag::all[] --> <div class="grid"> <!-- end::formTag[] --> <!-- end::head[] --> <div class="ingredient-group" id="wraps"> <!-- tag::designateWrap[] --> <h3>Designate your wrap:</h3> <div th:each="ingredient : ${wrap}"> <input name="ingredients" type="checkbox" th:value="${ingredient.id}" /> <span th:text="${ingredient.name}">INGREDIENT</span><br/> </div> <!-- end::designateWrap[] --> </div> <div class="ingredient-group" id="proteins"> <h3>Pick your protein:</h3> <div th:each="ingredient : ${protein}"> <input name="ingredients" type="checkbox" th:value="${ingredient.id}" /> <span th:text="${ingredient.name}">INGREDIENT</span><br/> </div> </div> <div class="ingredient-group" id="cheeses"> <h3>Choose your cheese:</h3> <div th:each="ingredient : ${cheese}"> <input name="ingredients" type="checkbox" th:value="${ingredient.id}" /> <span th:text="${ingredient.name}">INGREDIENT</span><br/> </div> </div> <div class="ingredient-group" id="veggies"> <h3>Determine your veggies:</h3> <div th:each="ingredient : ${veggies}"> <input name="ingredients" type="checkbox" th:value="${ingredient.id}" /> <span th:text="${ingredient.name}">INGREDIENT</span><br/> </div> </div> <div class="ingredient-group" id="sauces"> <h3>Select your sauce:</h3> <div th:each="ingredient : ${sauce}"> <input name="ingredients" type="checkbox" th:value="${ingredient.id}" /> <span th:text="${ingredient.name}">INGREDIENT</span><br/> </div> </div> </div> <div> <h3>Name your taco creation:</h3> <input type="text" th:field="*{name}"/> <!-- end::all[] --> <span th:text="${#fields.hasErrors('name')}">XXX</span> <span class="validationError" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</span> <!-- tag::all[] --> <br/> <button>Submit your taco</button> </div> <!-- tag::closeFormTag[] --> </form> <!-- end::closeFormTag[] --> </body> </html> <!-- end::all[] -->
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091Tips: 实际上,Thymeleaf在设计时是与Web框架解耦的,在Spring将请求发到视图之前,会先将属性复制到Servlet request的属性中,这样Thymeleaf就能访问到传过来的数据了。
<div th:each="ingredient : ${veggies}">,我们需要注意其中th:each标签,它会对${}中的元素集合(veggies)进行遍历,将每个元素都渲染<div>,每次迭代的时候,都会绑定到名为ingerdient的变量上。<span th:text="${ingredient.name}">INGREDIENT</span>,其中th:text需要我们注意一下,它会将ingredient.name中的值提取并显示出来。<link>引用,它是对图片和样式的引用,放在static目录,其中styles.css代码如下div.ingredient-group:nth-child(odd) { float: left; padding-right: 20px; } div.ingredient-group:nth-child(even) { float: left; padding-right: 0; } div.ingredient-group { width: 50%; } .grid:after { content: ""; display: table; clear: both; } *, *:after, *:before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } span.validationError { color: red; }
1234567891011121314151617181920212223242526272829@PostMapping public String processDesign(Taco design){//用日志保存提交的表单数据log.info("Processing design: " + design);return "redirect:/orders/current"; } 1234567 redirect:实现重定向,到/orders/current页面 6.1 实现对/orders/current的响应
编写OrdersController
package tacos; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @Slf4j @Controller @RequestMapping("/orders") public class OrderController { @GetMapping("/current") public String orderForm(Model model){ model.addAttribute("order",new Order()); return "orderForm"; } }
123456789101112131415161718192021创建Order实体类
package tacos; import lombok.Data; @Data public class Order { private String name; private String street; private String city; private String state; private String zip; private String ccNumber; private String ccExpiration; private String ccCVV; }
1234567891011121314151617编写orderForm.html
<!-- tag::allButValidation[] --> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Taco Cloud</title> <link rel="stylesheet" th:href="@{/styles.css}" /> </head> <body> <form method="POST" th:action="@{/orders}" th:object="${order}"> <h1>Order your taco creations!</h1> <img th:src="@{/images/TacoCloud.png}"/> <a th:href="@{/design}" id="another">Design another taco</a><br/> <div th:if="${#fields.hasErrors()}"> <span class="validationError"> Please correct the problems below and resubmit. </span> </div> <h3>Deliver my taco masterpieces to...</h3> <label for="name">Name: </label> <input type="text" th:field="*{name}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</span> <!-- tag::allButValidation[] --> <br/> <label for="street">Street address: </label> <input type="text" th:field="*{street}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('street')}" th:errors="*{street}">Street Error</span> <!-- tag::allButValidation[] --> <br/> <label for="city">City: </label> <input type="text" th:field="*{city}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('city')}" th:errors="*{city}">City Error</span> <!-- tag::allButValidation[] --> <br/> <label for="state">State: </label> <input type="text" th:field="*{state}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('state')}" th:errors="*{state}">State Error</span> <!-- tag::allButValidation[] --> <br/> <label for="zip">Zip code: </label> <input type="text" th:field="*{zip}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('zip')}" th:errors="*{zip}">Zip Error</span> <!-- tag::allButValidation[] --> <br/> <h3>Here's how I'll pay...</h3> <!-- tag::validatedField[] --> <label for="ccNumber">Credit Card #: </label> <input type="text" th:field="*{ccNumber}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('ccNumber')}" th:errors="*{ccNumber}">CC Num Error</span> <!-- tag::allButValidation[] --> <!-- end::validatedField[] --> <br/> <label for="ccExpiration">Expiration: </label> <input type="text" th:field="*{ccExpiration}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('ccExpiration')}" th:errors="*{ccExpiration}">CC Num Error</span> <!-- tag::allButValidation[] --> <br/> <label for="ccCVV">CVV: </label> <input type="text" th:field="*{ccCVV}"/> <!-- end::allButValidation[] --> <span class="validationError" th:if="${#fields.hasErrors('ccCVV')}" th:errors="*{ccCVV}">CC Num Error</span> <!-- tag::allButValidation[] --> <br/> <input type="submit" value="Submit order"/> </form> </body> </html> <!-- end::allButValidation[] -->
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 该表单中我们需要关注的命令如下,<form method="POST" th:action="@{/orders}" th:object="${order}">,对该表单提交后,发起POST请求,它特殊的地方在于,指定了action,若不指定action的情况下,则会返回与该表单相同的URL上,而当前指定了action,他则会跳转到“/orders”上。而现在我们的OrderController中没有方法实现对POST请求的处理,我们需要编写处理方法,如下
@PostMapping public String processOrder(Order order){//用日志进行保存log.info("Order submitted: " + order);return "redirect:/"; } 1234567
为了避免表单数据在提交时被乱输,我们可以在如下三方面进行验证
在要被校验的类上声明校验规则在控制器方法中声明校验修改表单,以表示校验错误@Data public class Taco { @NotNull @Size(min = 5,message = "Name Must Be At Least 5 characters long") private String name; @NotNull @Size(min = 1,message = "You must choose at least 1 ingredient") private List<String> ingredients; } 12345678910 @NotNull:不能让字段为空@Size:规定字段的长度要求
@Data public class Order { @NotBlank(message = "Name is required") private String name; @NotBlank(message = "Street is required") private String street; @NotBlank(message = "City is required") private String city; @NotBlank(message = "State is required") private String state; @NotBlank(message = "Zip is required") private String zip; @CreditCardNumber(message = "Not a valid credit card number") private String ccNumber; private String ccExpiration; //前者为整数位数,后者为小数位数 @Digits(integer = 3,fraction = 0,message = "Invalid CVV") private String ccCVV; }
1234567891011121314151617181920 @NotBlank:不能为空@CreditCardNumber:验证它是否为银行卡号@Digits:规定数字的规范,整数3位,小数0位在DesignTacoController中
@PostMapping public String processDesign(@Valid Taco design, Errors errors){ //有错误的情况下,回到当前表单,并在页面上显示错误信息 if(errors.hasErrors()){ return "design"; } //保存Taco信息 log.info("Processing design: " + design); //重定向 return "redirect:/orders/current"; } 12345678910111213
在OrderController中
@PostMapping public String processOrder(@Valid Order order, Errors errors){ //有错误的情况下,回到当前表单,并在页面上显示错误信息 if(errors.hasErrors()){ return "orderForm"; } log.info("Order submitted : " + order); return "redirect:/"; } 1234567891011 @Valid:该注解会告诉SpringMVC要对提交的Order对象进行检查,检查的时机在它绑定完表单数据之后,调用prodessOrder()方法之前。如果存在错误,则会捕捉到一个Errors对象,传递给prodessOrder()方法,通过if(errors.hasErrors())的判断,重新返回”design“,刷新页面,显示错误信息。
我们先将HomeController删除
随后创建如下类
@Configuration public class WebConfiguration implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("home"); } } 1234567 该控制器实现只转发视图而不做其他事情实现WebMvcConfigurer接口,重写addViewControllers()方法我们利用ViewControllerRegistry对象,调用addViewController()方法指定URL,调用setViewName()方法指定跳转的视图
同样我们可以利用最厉害的TacoCloudApplication来实现这个功能
@SpringBootApplication public class TacoCloudApplication implements WebMvcConfigurer { public static void main(String[] args) { SpringApplication.run(TacoCloudApplication.class, args); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("home"); } } 12345678910111213
这篇儿博客儿写的可读性差了一点儿,我觉得书里的写法也不如第一章清晰,加油儿吧!
相关知识
展示花艺的韩国Bring Spring精酿啤酒包装设计
4月课程 | 喷灌与快速水景实战班/庭院施工实战班,开始报名啦
摄影实战汇编
摄影技巧丨春暖花开,超实用的拍摄花卉实战技巧!
春天的花束 Spring Bouquet (1866),皮耶尔·奥古斯特·雷诺阿
【深度学习】 图像识别实战 102鲜花分类(flower 102)实战案例
春天里,难忘款冬花的那一抹金黄
基于Spring Boot的高校论坛交流系统的设计与实现(任务书)
春天开的花有哪些 开在春天里的花朵盘点
基于spring的花谷花卉科普系统的设计与实现
网址: “春天里的瑞士军刀”,《Spring实战》(二) https://m.huajiangbk.com/newsview138823.html
上一篇: 陌上花开 古风CG插画绘制技法精 |
下一篇: 苗木全冠是什么意思(解析苗木全冠 |