首页 > 其他分享 >Spring03:展现模型数据、处理及校验表单

Spring03:展现模型数据、处理及校验表单

时间:2022-10-01 12:55:36浏览次数:81  
标签:Spring03 校验 表单 tag design th Type public Ingredient

1 展现模型数据

1.1 图解过程

在上一节“Spring-02:构建并运行基础Spring应用”中,我们运行的基础应用所展现的视图home.html还未包含任何领域类的信息,如下图:

img

想要构建真实场景的web应用,我们就需要在前端页面展示后端的相关数据并能与之进行交互,这和我们之前学习的JavaWeb是一样的:Javaweb总结-目前开发Javaweb的套路梳理。那么使用Spring是如何构建的?它和之前我们学习的JavaWeb有什么区别?如果你认真学习并构建过Javaweb项目,经过本节学习,Spring的构建过程就能够非常清晰地理解了,并能更深刻地体会到Spring的强大之处。之前的Javaweb项目的请求流:

img

img

典型的Spring MVC请求流

image-20220930230048131

上图可以看出,之前我们构建Javaweb应用需要进行以下内容:

  • 构建中央控制器DispatcherServlet;

  • 构建不同业务对应的Controller;

  • 构建每个Controller对应的方法,并在方法中调用service、DAO层;

  • 然后将他们之前的关系手动配置到xml文件中;

  • 需要手动构建web.xml,配置里面对应的前端页面、thymeleaf视图模板的前后缀;

  • ......

简单回顾了一下我们之前构建应用的过程,是不是很复杂?再看到“典型的Spring MVC请求流图”,整个过程感觉非常简洁,它具体是怎样的过程呢?接下来通过一个简单的web应用来具体了解。

1.2 构建组件

1.2.1 视图

①home.html

<a th:href="@{/design}" id="design">Design a taco</a>

在原先的home页面我们需要加上一个超链接,来跳转到我们处理业务的页面。(通常home页面是登录页面,·需要将用户账户密码等信息进行校验,此处我们不进行复杂的处理,直接跳转。)

②design.html

头部

<head>
  <title>Taco Cloud</title>
  <link rel="stylesheet" th:href="@{/styles.css}" />
</head>

表单

<form method="POST" th:object="${design}">

 

<span class="validationError"
      th:if="${#fields.hasErrors('ingredients')}"
      th:errors="*{ingredients}">Ingredient Error</span>

业务内容(为你的taco选择

<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>

thymeleaf视图模板

之前的Javaweb项目我们了解过,它是一种服务器端模板技术,这里不过多介绍,只简单总结一下thymeleaf的语法,帮助大家更好的理解。

语法 描述
html xmlns:th="http://www.thymeleaf.org" thymeleaf名称空间,需要在页面html标签中指定
@{/...} 指定上下文路径,因为项目在不同环境部署时,Web应用的名字有可能发生变化,需要使用它来动态地获取
${…} 操作请求域、session 域和应用域的对象等值
#{…} 基本内置对象 p th:text="${#request.getClass().getName()}">这里显示#request对象的全类名</p
*{…} 获取上下文中对象的属性值
~{…} 片段表达式,jsp:include 作用,引入公共页面片段

● th:text :设置当前元素的文本内容,相同功能的还有 th:utext,两者的区别在于前者不会转义 html 标签,后者会。优先级不高:order=7

● th:value:设置当前元素的 value 值,类似修改指定属性的还有 th:src,th:href。优先 级不高:order=6

● th:each:遍历循环元素,和 th:text 或 th:value 一起使用。注意该属性修饰的标签位 置,详细往后看。优先级很高:order=2

● th:if:条件判断,类似的还有 th:unless,th:switch,th:case。优先级较高:order=3

● th:insert:代码块引入,类似的还有 th:replace,th:include,三者的区别较大,若使 用不恰当会破坏 html 结构,常用于公共代码块提取的场景。优先级最高:order=1

● th:fragment:定义代码块,方便被 th:insert 引用。优先级最低:order=8

● th:object:声明变量,一般和*{}一起配合使用,达到偷懒的效果。优先级一般:order=4

● th:attr:修改任意属性,实际开发中用的较少,因为有丰富的其他 th 属性帮忙,类 似的还有 th:attrappend,th:attrprepend。优先级一般:order=5 深入学习请参考:https://blog.csdn.net/qq_44981526/article/details/126211200

或者直接根据本文中的示例结合自己的项目来理解

2.1.2 领域类

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
  }
​
}

2.1.3 控制器类

DesignTacoController

@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {
​
​
//end::head[]
​
@ModelAttribute
public void addIngredientsToModel(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 = Ingredient.Type.values();
   for (Type type : types) {
     model.addAttribute(type.toString().toLowerCase(),
         filterByType(ingredients, type));
   }
}
   
//tag::showDesignForm[]
  @GetMapping
  public String showDesignForm(Model model) {
    model.addAttribute("design", new Taco());
    return "design";
  }
​
//end::showDesignForm[]
​
/*
//tag::processDesign[]
  @PostMapping
  public String processDesign(Design design) {
    // Save the taco design...
    // We'll do this in chapter 3
    log.info("Processing design: " + design);
​
    return "redirect:/orders/current";
  }
​
//end::processDesign[]
 */
​
//tag::processDesignValidated[]
  @PostMapping
  public String processDesign(@Valid @ModelAttribute("design") Taco design, Errors errors, Model model) {
    if (errors.hasErrors()) {
      return "design";
    }
​
    // Save the taco design...
    // We'll do this in chapter 3
    log.info("Processing design: " + design);
​
    return "redirect:/orders/current";
  }
​
//end::processDesignValidated[]
​
//tag::filterByType[]
  private List<Ingredient> filterByType(
      List<Ingredient> ingredients, Type type) {
    return ingredients
              .stream()
              .filter(x -> x.getType().equals(type))
              .collect(Collectors.toList());
  }
​
//end::filterByType[]
// tag::foot[]
}

OrderController

import javax.validation.Valid;
​
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
//end::baseClass[]
import org.springframework.web.bind.annotation.PostMapping;
//tag::baseClass[]
import org.springframework.web.bind.annotation.RequestMapping;
​
import lombok.extern.slf4j.Slf4j;
import tacos.Order;
​
@Slf4j
@Controller
@RequestMapping("/orders")
public class    OrderController {
  
//end::baseClass[]
//tag::orderForm[]
  @GetMapping("/current")
  public String orderForm(Model model) {
    model.addAttribute("order", new Order());
    return "orderForm";
  }
//end::orderForm[]
​
/*
//tag::handlePost[]
  @PostMapping
  public String processOrder(Order order) {
    log.info("Order submitted: " + order);
    return "redirect:/";
  }
//end::handlePost[]
*/
  
//tag::handlePostWithValidation[]
  @PostMapping
  public String processOrder(@Valid Order order, Errors errors) {
    if (errors.hasErrors()) {
      return "orderForm";
    }
    
    log.info("Order submitted: " + order);
    return "redirect:/";
  }
//end::handlePostWithValidation[]
  
//tag::baseClass[]
  
}
//end::baseClass[]

2 处理请求

2.2.1 Get

前端:

①发送get请求,访问http://localhost:8080/design

控制器:

//tag::showDesignForm[]
  @GetMapping
  public String showDesignForm(Model model) {
    model.addAttribute("design", new Taco());
    return "design";
  }

②Spring通过@Controller注解,发现DesignTacoController这个控制器,会自动创建他的实例,作为上下文中的bean;

③通过@RequestMapping("/design")注解,DesignTacoController处理"/design"开头的请求;

④通过@GetMapping注解,调用showDesignForm,创建一个taco对象,将配料类型列表作为属性添加到Model对象中(Model对象负责在控制器和展现数据的视图之间传递数据)

⑤通过我们定义的thymeleaf视图,将我们添加到model中的对象的属性渲染展现在页面上。

2.2.2 Post

前端

<form method="POST" th:object="${design}">

①在页面填写好表单后,点击submit按钮提交该表单发送post请求;

  public String processDesign(@Valid @ModelAttribute("design") Taco design, Errors errors, Model model) {
    if (errors.hasErrors()) {
      return "design";
    }
​
    // Save the taco design...
    // We'll do this in chapter 3
    log.info("Processing design: " + design);
    
    return "redirect:/orders/current";
​
  }

②和@GetMapping类似,通过@PostMapping来处理POST请求,

③再通过@ModelAttribute注解,调用addIngredientsToModel(Model model)方法,读取我们填写的Ingredient属性值;

④重定向"redirect:/orders/current",跳转http://localhost:8080/orders/current页面(对应OrderController控制器)

3 校验表单

在processDesign方法参数(@Valid @ModelAttribute("design") Taco design, Errors errors, Model model)中我们看到了@Valid注释,它表示我们要对在post请求的表单中提交的Taco对象进行验证。

3.1 Validation注解

Spring支持java的Bean校验API-JSR 380定义了一些注解用于做数据校验,这些注解可以直接设置在Bean属性上

  • @NotNull不允许外null对象

  • @AssertTrue是否为true

  • @Size约定字符串长度

  • @Min字符串的最小长度

  • @Max字符串的最大长度

  • @Email是否为邮箱格式

  • @NotEmpty不允许为null或者空,可以用于判断字符串、集合,比如Map,数组,List

  • @NotBlank不允许为null和空格

本案例应用如下:

@Data
public class Taco {
​
  // end::allButValidation[]
  @NotNull
  @Size(min=5, message="Name must be at least 5 characters long")
  // tag::allButValidation[]
  private String name;
  // end::allButValidation[]
  @Size(min=1, message="You must choose at least 1 ingredient")
  // tag::allButValidation[]
  private List<String> ingredients;
​
}

3.2 展现表单错误

<span class="validationError"
      th:if="${#fields.hasErrors('ingredients')}"
      th:errors="*{ingredients}">Ingredient Error</span>

这是由thymeleaf提供的便捷访问Error对象的方法,如果ingredients输入域存在错误,那么它会将<span>元素的占位符替换为校验信息。

效果如下:


参考资料:

《Spring实战第5版》

标签:Spring03,校验,表单,tag,design,th,Type,public,Ingredient
From: https://www.cnblogs.com/fancy2022/p/16747070.html

相关文章

  • 痞子衡嵌入式:一个关于Segger J-Flash在Micron Flash固定区域下载校验失败的故事(SR寄
    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家讲的是一个关于SeggerJ-Flash在MicronFlash固定区域下载校验失败的故事。痞子衡最近在支持一个i.MXRT......
  • Demo:校验
    货铺QQ群号:834508274下面开始干货:tcode:GGB0设置先决条件设置检查点这里ZU01对应的代码是需要配置维护的。不然是显示不出来ZU01的。ZU01代码的相关配置维护点:Tcode:GCX2Co......
  • 除了OA、ERP、表单生成器、协同办公,能够做整个应用的低代码平台有吗?
    其实OA、ERP等也是可以在低代码平台做的,只是能做这种定制的要求比较高,所以这种平台少且必须是原子级定制的。而大多数低代码平台只是基于定制好的模块来完成模块的拼接或者......
  • 通过一文简单了解自定义表单设计器
    什么是自定义表单设计器?大家对这个问题还是比较关注的。其实,自定义表单设计器是一款在线可视化表单建模工具,组件丰富、操作简单、灵活更方便,是很多现代化企业倾向于使用的......
  • 12. HTML-- 表单:<form>标签
    1.前言当您想要通过网页来收集一些用户的信息(例如用户名、电话、邮箱地址等)时,就需要用到HTML表单。表单可以接收用户输入的信息,然后将其发送到后端应用程序,例如PHP、J......
  • JavaScript——案例-表单验证
    需求     <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body><formid="reg-form"action="#"......
  • 图形校验码绕过的几个小技巧
    1、重复发送数据包,观察图形验证码是否可重复利用2、如果登录报错会有弹框提示,在不关闭弹窗情况下,在burp中重复发送数据包3、删除图形验证码参数4、将图形验证码参数值置空5......
  • Flask 学习-81.Flask-RESTX使用reqparse 解析器校验枚举类型choices 参数
    前言reqparse.RequestParser()解析器可以校验枚举类型,在add_argument中使用choices参数choices设置参数可选值比如性别设置可选项:男、女defpost(self):......
  • FormData将HTML表单转换为formData表单对象
    FormData对象的作用模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式。异步上传二进制文件FormData对象的使用<!DOCTYPEht......
  • 拖拽表单设计器可以提高办公效率?
    如果要在印刷设计模式和拖拽式表单设计器中间做一个选择,到底哪一款才是更适合现代企业发展需求的软件?如果想提升开发效率和办公效率,那么,拖拽表单设计器就是一种很好的体验......