[spring] Spring MVC Thymeleaf(下)

[spring] Spring MVC & Thymeleaf(下)

上篇笔记讲了一下怎么使用 thymeleaf 作为 HTML 模板,与 Spring MVC 进行沟通,这里主要说一下验证的部分

常用表单验证

一些 Spring MVC 内置的常用验证注解如下:

AnnotationDescription
@NotNullEnsures the annotated field is not null.
@NotEmptyEnsures the annotated field is not empty. This applies to Strings, Collections, Maps, and arrays.
@NotBlankEnsures the annotated string field is not null or empty.
@SizeValidates that the annotated field has a size within the specified bounds.
@MinValidates that the annotated field has a value no smaller than the specified minimum.
@MaxValidates that the annotated field has a value no larger than the specified maximum.
@EmailValidates that the annotated field is a valid email address.
@PatternValidates that the annotated field matches the specified regular expression.
@PastValidates that the annotated field contains a date in the past.
@FutureValidates that the annotated field contains a date in the future.
@PositiveValidates that the annotated field is a positive number.
@PositiveOrZeroValidates that the annotated field is a positive number or zero.
@NegativeValidates that the annotated field is a negative number.
@NegativeOrZeroValidates that the annotated field is a negative number or zero.
@DigitsValidates that the annotated field is a number within the specified integer and fraction digits.
@DecimalMinValidates that the annotated field is a decimal number no smaller than the specified minimum.
@DecimalMaxValidates that the annotated field is a decimal number no larger than the specified maximum.
@AssertTrueValidates that the annotated field is true.
@AssertFalseValidates that the annotated field is false.
@CreditCardNumberValidates that the annotated field is a valid credit card number.

这个列表只是做个参考,具体用法大同小异,后面会选几个比较常用的实现一下,以及会实现自定义验证

设置开发环境

依旧从 spring.io 下载,具体配置如下:

在这里插入图片描述

和上里的内容比起来,这里多了一个 Validator

必填选项

这里的实现步骤如下:

  1. 创建一个 DAO,并添加验证规则

    实现代码如下:

    package com.example.springdemo.mvc;
    
    import jakarta.validation.constraints.NotNull;
    import jakarta.validation.constraints.Size;
    import lombok.Data;
    
    @Data
    public class Customer {
        private String firstName;
    
        @NotNull(message = "is required")
        @Size(min = 1, message = "is required")
        private String lastName;
    }
    
    

    ⚠️:这里的 @NotNull@Size 是从 jakarta.validation.constraints 中导入的。这里的 message 就是验证失败后,会返回的错误信息

    👀:这里其实有一个小问题,就是如果用户提供的数据是空的字符串,那么也是可以通过验证的。空字符串的处理在后面会提到

  2. 添加 controller

    package com.example.springdemo.mvc;
    
    import jakarta.validation.Valid;
    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.ModelAttribute;
    import org.springframework.web.bind.annotation.PostMapping;
    
    @Controller
    public class CustomerController {
        @GetMapping("/")
        public String showForm(Model model) {
            model.addAttribute("customer", new Customer());
            return "customer-form";
        }
    
        @PostMapping("/processForm")
        public String processForm(
                @Valid @ModelAttribute("customer") Customer customer,
                BindingResult bindingResult
        ) {
            if (bindingResult.hasErrors()) {
                return "customer-form";
            }
    
            return "customer-confirmation";
        }
    }
    
    

    这里其实直接把第 4 步也做了,后面会具体提一下实现的功能

  3. 添加 HTML 和验证支持

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
        <meta charset="UTF-8" />
        <title>Customer Registration Form</title>
        <style>
          .error {
            color: red;
          }
        </style>
      </head>
      <body>
        <i>Fill out the form</i>
    
        <br /><br />
    
        <form
          th:action="@{/processForm}"
          th:object="${customer}"
          method="post"
          autocomplete="off"
        >
          <label>
            First Name:
            <input type="text" th:field="*{firstName}" />
          </label>
          <br /><br />
          <label>
            Last Name (*):
            <input type="text" th:field="*{lastName}" />
          </label>
    
          <!--  add error message (if present)  -->
          <span
            th:if="#{fields.hasErrors('lastName')}"
            th:errors="*{lastName}"
            class="error"
          ></span>
    
          <br /><br />
    
          <input type="submit" value="Submit" />
        </form>
    
        <script src="http://localhost:35729/livereload.js"></script>
      </body>
    </html>
    

    这里的代码和之前实现的 thymeleaf 没有什么特别大的区别,唯一的差别在于用于显示错误信息的 span 这部分

    th:if 的用法之前已经提过,这里不多赘述

    #fields 是一个内置的工具对象,可以用来检查哪个属性有错。在这个情况下,它会检查 lastName 是否验证失败

    th:errors="*{lastName}" 是一个没见过的 thymeleaf 语法,这里主要就是将报错信息渲染到 DOM 中

    class="error" 用于添加 css

  4. 在 controller 中实现验证

    @PostMapping("/processForm")
          public String processForm(
                  @Valid @ModelAttribute("customer") Customer customer,
                  BindingResult bindingResult
          ) {
              if (bindingResult.hasErrors()) {
                  return "customer-form";
              }
    
              return "customer-confirmation";
          }
    

    这里着重讲一下这部分的代码

    @Valid 注解代表 customer 对象包含验证需要被执行,这也是在 DAO 层中,在 lastName 上添加的验证

    BindingResult bindingResult 也是通过依赖注入获取的,这个对象会绑定所有的验证结果

  5. 创建 confirm 页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
        <meta charset="UTF-8" />
        <title>Customer Confirmation</title>
      </head>
      <body>
        The customer is confirmed:
        <span th:text="${customer.firstName + ' ' + customer.lastName}"></span>
    
        <script src="http://localhost:35729/livereload.js"></script>
      </body>
    </html>
    

    这部分没什么特殊的,之前已经写过很多遍了

具体的实现效果如下:

在这里插入图片描述

@InitBinder

上面的实现中,如果用户输入空白的字符串,那么也会通过检查。因此这里会使用 @InitBinder 去对数据进行一个自定义处理(预处理)

具体的使用方式如下:

public class CustomerController {
    @InitBinder
    public void initBinder(WebDataBinder dataBinder) {
        StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);

        dataBinder.registerCustomEditor(String.class, stringTrimmerEditor);
    }
}

这部分其实没有什么特别复杂的,WebDataBinder 是一个数据绑定器,传入的参数也是需要处理的数据类型,与处理的方式。如这里的实现就是对所有输入的字符串进行一个处理,处理的方式就是将空格去除

需要注意的就是,这部分的代码需要在 controller 中实现,这里的代码就是在 CustomerController 中实现的

@Min & @Max

@Min & @Max 也是两个常见的对数字进行的验证,实现方法如下:

@Data
public class Customer {
    @Min(value = 0, message = "must be greater than or equals to 0")
    @Max(value = 10, message = "must be smaller than or equals to 10")
    private int freePasses;
}

这里不需要修改 controller,HTML 模板的修改部分如下:

<label>
  Free passes:
  <input type="text" th:field="*{freePasses}" />
</label>

<!--  add error message (if present)  -->
<span
  th:if="#{fields.hasErrors('freePasses')}"
  th:errors="*{freePasses}"
  class="error"
></span>

<br /><br />

实现效果:

在这里插入图片描述

regex,正则

验证同样可以接受正则,这里只做一个比较简单的处理方式,即邮政编码的处理,可以包含数字和大小写字母

DAO 修改如下:

    @Pattern(regexp = "^[a-zA-Z0-9]{5}", message = "only 5 chars/digits")
    private String postalCode;

同样不需要修改 controller,只需要更新 HTML 模板:

<label>
  Postal Code
  <input type="text" th:field="*{postalCode}" />
</label>

<!--  add error message (if present)  -->
<span
  th:if="#{fields.hasErrors('postalCode')}"
  th:errors="*{postalCode}"
  class="error"
></span>

这是 confirmation 的修改:

Postal Code: <span th:text="${customer.postalCode}"></span>

显示效果:

在这里插入图片描述

数字的一些问题

@NotNull

数字类型使用使用 @NotNull 会造成一点问题,如:

在这里插入图片描述

这是因为 Java 的数据类型转换问题,其中一个解决方式可以吧声明的 private int freePasses; 换成 private Integer freePasses;,这样 Integer 这个包装类可以自动实现数据转换的处理

在这里插入图片描述

字符串

另一个可能会遇到的问题,就是在处理数字的输入框中输入字符串,如:

在这里插入图片描述

这种情况下,Spring 也是没有办法正常转换类型,这里可以解决的方法是在 resource 文件夹下,新建一个 messages.properties 文件

这是一个特殊的 properties 文件,Spring 会自动扫描并读取这个文件,并可以通过这个文件信息进行处理。除了报错信息外,它也经常被用于 i18n 和 l10n

配置方式如下:

typeMismatch.customer.freePasses=Invalid number

实现效果:

在这里插入图片描述

在这里插入图片描述

⚠️:使用这个配置文件也可以覆盖上面 @NotNull 带来的问题,主要因为空字符串也不是一个合法的数字,所以这个错误也会被 typeMismatch 所捕获

自定义验证

Spring 提供的验证肯定没有办法满足所有的需求,这个情况下就需要开发手动实现自定义验证

具体的实现步骤如下:

  • 创建一个自定义验证规则

    这里会使用 @interface 去创建一个注解的 interface,并且实现注解

    package com.example.springdemo.mvc.validation;
    
    import jakarta.validation.Constraint;
    import jakarta.validation.Payload;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Constraint(validatedBy = CourseCodeConstraintValidator.class)
    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CourseCode {
        public String value() default "CS";
    
        public String message() default "must start with CS";
    
        public Class<?>[] groups() default {};
    
        public Class<? extends Payload>[] payload() default {};
    }
    
    

    注解实现:

    package com.example.springdemo.mvc.validation;
    
    import jakarta.validation.ConstraintValidator;
    import jakarta.validation.ConstraintValidatorContext;
    
    public class CourseCodeConstraintValidator  implements ConstraintValidator<CourseCode, String> {
        private String coursePrefix;
    
        @Override
        public void initialize(CourseCode courseCode) {
            coursePrefix = courseCode.value();
        }
    
        @Override
        public boolean isValid(String code, ConstraintValidatorContext constraintValidatorContext) {
            // handle null value, since when user doesn't provide any input, code will be null
            if (code != null) return code.startsWith(coursePrefix);
    
            return false;
      }
    }
    
    

    到这一步就可以使用新实现的自定义验证了

    注解的部分不会涉及太多……主要是我也不太熟。等后面重新学一些 java 的新特性的时候再研究叭

  • Customer 中使用心得自定义验证

    @Data
    public class Customer {
        // omit other impl
    
        // it will take default value
        @CourseCode
        // can be overwritten in this way:
        // @CourseCode(value = "something", message = "must start with example")
        private String courseCode;
    }
    

    这里是用的就是默认的信息

    不使用默认信息的方法在注释掉的代码里

后面的步骤,就是更新 HTML 模板的部分就省略了,最终的实现效果如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/743347.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis 7.x 系列【6】数据类型之字符串(String)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 前言2. 常用命令2.1 SET2.2 GET2.3 MSET2.4 MGET2.5 GETSET2.6 STRLEN2.7 SETEX2.8…

8.12 矢量图层面要素单一符号使用七(随机标记填充)

文章目录 前言随机标记填充&#xff08;Random Marker Fill&#xff09;QGis设置面符号为随机标记填充&#xff08;Random Marker Fill&#xff09;二次开发代码实现随机标记填充&#xff08;Random Marker Fill&#xff09; 总结 前言 本章介绍矢量图层线要素单一符号中使用随…

解决node: bad option: -V

出现这个问题是由于我们的不当操作造成的&#xff0c;v是需要小写的&#xff0c;看下图 node --version node -v

KT6368A-sop8蓝牙主机芯片获取电动车胎压传感器数据功能

KT6368A蓝牙芯片新增主机模式&#xff0c;扫描周边的胎压传感器&#xff0c;这里扮演的角色就是观察者。因为测试胎压传感器&#xff0c;发现它的广播模式可发现&#xff0c;不可连接 胎压传感器部分的手册说明如下&#xff0c;关于蓝牙部分的协议 实际蓝牙芯片收到的数据&…

JavaWeb系列十一: Web 开发会话技术(Cookie, Session)

韩sir Cookie技术Cookie简单示意图Cookie常用方法Cookie创建Cookie读取JSESSIONID读取指定Cookie Cookie修改Cookie生命周期Cookie的有效路径Cookie作业布置Cookie注意事项Cookie中文乱码问题 Session技术Session原理示意图Session常用方法Session底层机制Session生命周期Sessi…

群智优化:探索BP神经网络的最优配置

群智优化&#xff1a;探索BP神经网络的最优配置 一、数据集介绍 鸢尾花数据集最初由Edgar Anderson测量得到&#xff0c;而后在著名的统计学家和生物学家R.A Fisher于1936年发表的文章中被引入到统计和机器学习领域数据集特征&#xff1a; 鸢尾花数据集包含了150个样本&#…

【激光雷达使用记录】—— 如何在ubuntu中利用ros自带的rviz工具实时可视化雷达点云的数据

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、查看雷达数据的 frame_id1. 查看雷达数据的话题2. 查看数据的frame_id 二、可视化雷达数据总结 前言 RViz&#xff08;ROS Visualization&#xff09;是机…

Etsy店铺销量持续增长?揭秘我的稳定运营秘诀

一、什么是Esty&#xff1f; 今天的这篇文章应该是很多Etsy的卖家都十分关心的。相信了解过Etsy平台的家人们都知道&#xff0c;Etsy主要是一个专注于手工制品、古董和创意商品的电子商务平台&#xff0c;更是全球利润最高的跨境电商平台。利润高就会吸引更多的人前来注册店铺…

音视频入门基础:H.264专题(4)——NALU Header:forbidden_zero_bit、nal_ref_idc、nal_unit_type简介

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…

数字化转型第三步:数字化业务创新与发展,提升收入和利润

引言&#xff1a;之前笔者的文章发布了企业数字化转型业务部分&#xff0c;如【开源节流】如何通过数字化转型增强盈利能力&#xff1f;企业供应链数字化转型如何做&#xff1f;让企业盈利能力增强再飞一会 【财务数字化转型之底座】集团企业财务数据中台系统建设方案 等文章&a…

Win10可用的VC6.0绿色版及辅助插件assist_X

VC6.0&#xff0c;作为微软的经典开发工具&#xff0c;承载着无数开发者的青春与回忆。它曾是Windows平台上软件开发的重要基石&#xff0c;为开发者们提供了稳定且强大的编程环境&#xff0c;尤其是其MFC&#xff08;Microsoft Foundation Classes&#xff09;库&#xff0c;为…

构建实用的Flutter文件列表:从简到繁的完美演进

前言&#xff1a;为什么我们需要文件列表&#xff1f; 在现代科技发展迅速的时代&#xff0c;我们的电脑、手机、平板等设备里积累了大量的文件&#xff0c;这些文件可能是我们的照片、文档、音频、视频等等。然而&#xff0c;当文件数量增多时&#xff0c;我们如何快速地找到…

eNSP中三层交换机的配置和使用

一、拓扑图 1.新建拓扑图 2.PC端配置 PC1: PC2&#xff1a; 二、基本命令配置 1.S1配置 <Huawei>system-view [Huawei]sysname S1 [S1]vlan 10 //在交换机 S1 上创建 VLAN 10 [S1-vlan10]vlan 20 // 在交换机 S1 上创建 VLAN 20 [S1-vlan20]quit //退出 VLAN 配置…

使用Spring Boot实现博客管理系统

文章目录 引言第一章 Spring Boot概述1.1 什么是Spring Boot1.2 Spring Boot的主要特性 第二章 项目初始化第三章 用户管理模块3.1 创建用户实体类3.2 创建用户Repository接口3.3 实现用户Service类3.4 创建用户Controller类 第四章 博客文章管理模块4.1 创建博客文章实体类4.2…

探究InnoDB Compact行格式背后

目录 一、InnoDB 行格式数据准备 二、COMPACT行格式整体说明 三、记录的额外信息 &#xff08;一&#xff09;变长字段长度列表 数据结构 存储过程 读取过程 变长字段长度列表存储示例 &#xff08;二&#xff09;NULL 值位图 数据结构 存储过程 读取过程 NULL 值…

【MySQL进阶之路 | 高级篇】索引的声明与使用

1. 索引的分类 MySQL的索引包括普通索引&#xff0c;唯一性索引&#xff0c;全文索引&#xff0c;单列索引和空间索引. 从功能逻辑上说&#xff0c;索引主要分为普通索引&#xff0c;唯一索引&#xff0c;主键索引和全文索引.按物理实现方式&#xff0c;索引可以分为聚簇索引…

光伏开发有没有难点?如何解决?

随着全球对可再生能源的日益重视&#xff0c;光伏技术作为其中的佼佼者&#xff0c;已成为实现能源转型的关键手段。然而&#xff0c;光伏开发并非一帆风顺&#xff0c;其过程中也面临着诸多难点和挑战。本文将对这些难点进行探讨&#xff0c;并提出相应的解决策略。 一、光伏开…

基于SSM构建的校园失眠与压力管理系统的设计与实现【附源码】

毕业设计(论文) 题目&#xff1a;基于SSM构建的校园失眠与压力管理系统的设计与实现 二级学院&#xff1a; 专业(方向)&#xff1a; 班 级&#xff1a; 学 生&#xff1a; 指导教师&a…

【启明智显产品介绍】Model3C工业级HMI芯片详解专题(三)通信接口

Model3C 是一款基于 RISC-V 的高性能、国产自主、工业级高清显示与智能控制 MCU, 集成了内置以太网控制器&#xff0c;配备2路CAN、4路UART、5组GPIO、2路SPI等多种通信接口&#xff0c;能够轻松与各种显示设备连接&#xff0c;实现快速数据传输和稳定通信&#xff0c;可以与各…

路由(urls)

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 Django的URL路由流程&#xff1a; l Django查找全局urlpatterns变量&#xff08;urls.py&#xff09;。 l 按照先后顺序&#xff0c;对URL逐一匹…