概述

在 Web 应用中,客户端提交数据之前都会进行数据的校验,比如用户注册时填写的邮箱地址是否符合规范、用户名长度的限制等等,不过这并不意味着服务端的代码可以免去数据验证的工作,用户也可能使用 HTTP 工具直接发送违法数据。为了保证数据的安全性,服务端的数据校验是必须的。

先理清概念:

  • JSR-303 是 JavaEE 6 中的一项子规范,又称作 Bean Validation,提供了针对 Java Bean 字段的一些校验注解,如@NotNull@Min等。JSR-349 是其升级版本,添加了一些新特性。
  • Hibernate Validator 是对这个规范的实现(与 ORM 框架无关),并在它的基础上增加了一些新的校验注解。
  • Spring 本身也有一个校验接口Validator,位于 org.springframework.validation 包下,但是使用这个接口需要进行硬编码,也就是手动校验,没有提供注解进行简化。为了给开发者提供便捷,Spring 也全面支持 JSR-303、JSR-349 规范,对 Hibernate Validation 进行二次封装,在 SpringMVC 模块中添加了自动校验机制,可以利用注解对 Java Bean 的字段的值进行校验,并将校验信息封装进特定的类中。

下面将介绍如何在 Spring 应用中使用 JSR-303 校验规范。

校验注解

1. JSR-303 包含的注解

注解名称 说明
@Null 被注解元素必须为 null
@NonNull 被注解元素必须不为 null
@AssertTrue 被注解元素必须为 true
@AssertFalse 被注解元素必须为 false
@Min(value) 被注解元素必须是一个值,并且不能小于指定的值
@Max(value) 被注解元素必须是一个值,并且不能大于指定的值
@DecimalMin(value) 被注解元素必须是一个数字,并且不能小于指定的值
@DecimalMax(value) 被注解元素必须是一个数字,并且不能大于指定的值
@Size(max=,min=) 被注解元素的大小必须在指定范围内
@Digits(integer,fraction) 被注解元素必须是一个数字,其值必须在指定范围内
@Past 被注解元素必须是一个过去的日期
@Future 被注解元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注解元素必须符合指定的正则表达式

2. Hibernate Validator 扩展的注解

注解名称 说明
@NotBlank(message=) 被注解的字符串必须非 null 且trim()后长度大于 0
@Email 被注解元素必须是电子邮箱地址
@Length(min=,max=) 被注解的字符串的长度必须在指定范围内
@NotEmpty 被注解元素(字符串、数组、集合等)必须非 null 且长度大于 0
@Range(min=,max=,message=) 被注解元素必须在合适的范围内
@URL 被注解元素必须是合法的 URL

用法

1. 依赖

pom.xml中引入 Hibernate Validator 需要的依赖包:


<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.10.Final</version> </dependency>

如果你使用的是 SpringBoot,那么只需引入spring-boot-starter-web即可,它的子依赖中包含了hibernate-validator和必要的数据绑定组件。

2. 实体类


public class Student { private Long id; @NotBlank(message = "名称不能为空") private String name; @Range(min = 10, max = 25, message = "年龄必须在10~25之间 ") private Integer age; @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$", message = "手机号码格式错误") @NotBlank(message = "手机号码不能为空") private String phone; @Email(message = "邮箱格式错误") private String email; // Getter/Setter }

每一个注解都包含了message字段,用于校验失败时作为提示信息。

3. 处理器方法

在处理器方法需要校验的参数上添加@Valid注解,就可以“激活”对它的校验操作,后面传入一个 BindingResult 类型的参数,用于获取校验失败情况下的反馈信息。


@PostMapping("/student") public ApiResult addStudent(@RequestBody @Valid Student student, BindingResult result) { if (result.hasErrors()) { for (ObjectError error : result.getAllErrors()) { System.out.println(error.getDefaultMessage(); } return ApiResult.error(); } studentService.insert(student); return ApiResult.success(); }

@Valid注解的参数和 BindingResult 参数必须是成对出现的,并且一前一后。

发送数据,进入上面的处理器:

控制台输出:

自定义校验

假如现在有一个需求是学生名字的内部不能含有空格,我们要如何自定义一个满足该要求的校验呢?

1. 自定义注解


@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
@Constraint(validatedBy = NotContainSpaceValidator.class)
public @interface NotContainSpace {
//默认错误消息
String message() default "不能包含空格"; //分组
Class<?>[] groups() default {}; //负载
Class<? extends Payload>[] payload() default {}; //指定多个时使用,从而支持重复注解
@Target({FIELD, METHOD, PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
NotContainSpace[] value();
}
}

@Constraint指定这个注解真正的校验者类。

2. 校验者类

实现ConstraintValidator接口:


public class NotContainSpaceValidator implements ConstraintValidator<NotContainSpace, String> {
@Override
public void initialize(NotContainSpace notContainSpace) {
} @Override
public boolean isValid(String s, ConstraintValidatorContext context) {
if (s != null && s.trim().contains(" ")) {
// 获取默认提示信息
String constraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
System.out.println(constraintMessageTemplate); // 禁用默认提示信息
context.disableDefaultConstraintViolation(); // 设置提示语
context.buildConstraintViolationWithTemplate("Can not contain space").addConstraintViolation();
return false;
}
return true;
}
}

第一个泛型参数是表明校验的注解类型,第二个泛型参数是需要被校验的类型。

  • initialize:初始化事件方法
  • isValid:判断是否合法的方法

ConstraintValidatorContext这个上下文包含了校验中所有的信息,我们可以利用这个对象进行获取默认错误提示信息,禁用错误提示信息,改写错误提示信息等操作。

4. 使用


@NotContainSpace
private String name;

发送数据:

控制台输出:

可以看到NotContainSpaceValidator的执行是先于处理器方法的,并且禁用默认提示语、设置新的提示语等操作都是有效的。

分组校验

如果对同一个类,在不同的使用场景下有不同的校验规则,就可以使用分组校验。比如更新一条学生记录时,要求 id 不为 null 且大于 0:

我们上面在对方法参数添加自动校验时用的是@Valid注解,它是由 javax 提供的,其实 Spring Validation 校验框架还提供了@Validated注解。在检验 Controller 的入参是否符合规范时,它们的基本验证功能并没有多大区别。但是,现在要进行分组校验,就必须使用@Validated:


@PutMapping("/student")
public ApiResult updateStudent(@RequestBody @Validated({Student.Update.class}) Student student, BindingResult result) {
if (result.hasErrors()) {
for (ObjectError error : result.getAllErrors()) {
// do stuff
}
return ApiResult.error();
}
studentService.update(student);
return ApiResult.success();
}

不过注意,在@Validated中指定了分组,那么其他未分组的校验将会被忽略。

关于更多 @Valid 和 @Validated 的区别,可见文末参考链接。

END

Spring Validation 校验的更多相关文章

  1. 使用spring validation完成数据后端校验

    前言 数据的校验是交互式网站一个不可或缺的功能,前端的js校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的校验.但是为了避免用户绕过浏览器,使用http工具直接向后端请求 ...

  2. Spring validation 后端校验【转】

    本文来自 下一秒升华 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/u013815546/article/details/77248003?utm_source=co ...

  3. 使用spring validation完成数据后端校验-自定义校验的注解-判断是否为空

    引入依赖 我们使用maven构建springboot应用来进行demo演示. <dependencies> <dependency> <groupId>org.sp ...

  4. 使用Spring Validation优雅地校验参数

    写得好的没我写得全,写得全的没我写得好 引言 不知道大家平时的业务开发过程中 controller 层的参数校验都是怎么写的?是否也存在下面这样的直接判断? public String add(Use ...

  5. Spring Validation最佳实践及其实现原理,参数校验没那么简单!

    之前也写过一篇关于Spring Validation使用的文章,不过自我感觉还是浮于表面,本次打算彻底搞懂Spring Validation.本文会详细介绍Spring Validation各种场景下 ...

  6. Spring Validation 表单校验

    最近开发一个项目,发现字段校验比较多,同事提出使用Validation校验能更方便一些,今天记录下来,供以后参考: 一.在pom.xml中添加依赖: <dependency> <gr ...

  7. Spring表单验证(Spring Validation)

    1.基本介绍 之前在项目中做的后台验证就是Spring Validation,最近闲下来了,就来整理一下. 从Spring3.0开始,Spring MVC中提供了对java校验的API支持.在Spri ...

  8. JSR303/JSR-349,hibernate validation,spring validation 之间的关系

    JSR303是一项标准,JSR-349是其的升级版本,添加了一些新特性,他们规定一些校验规范即校验注解,如@Null,@NotNull,@Pattern,他们位于javax.validation.co ...

  9. # SpringBoot使用Validation校验参数 ##

    SpringBoot使用Validation校验参数 一.简介 参考 (14条消息) 1. 不吹不擂,第一篇就能提升你对Bean Validation数据校验的认知_@decimalmax和@max_ ...

  10. springboot使用hibernate validator校验,Bean Validation校验

    第一个地址:springboot使用hibernate validator校验,Bean Validation校验

随机推荐

  1. 介绍this指向问题

    this是js底层定义的变量,代表了代码的指向环境 a 函数的this是window b 方法的this是调用的对象 c 构造函数和原型对象上的方法的this指向实例化对象 d 箭头函数不会自己创建t ...

  2. 解决 WebSocketClient.js?5586:16 WebSocket connection to 'ws://192.168.13.25:8080/ws' failed:

    控制台报错: vue.config.js Vue的配置文件 const { defineConfig } = require('@vue/cli-service') module.exports = ...

  3. idea创建搭建项目 maven eg

    1. 创建一个空的项目 ps:作为 git 管理 ,父项目 2. 创建第一个微服务 先导入两个必要的组件 web spring web : spring cloud openfeign (用于微服务之 ...

  4. Cookie、sessionStorage、localStorage的区别 ?

    共同点:都是保存在浏览器端的. 区别: 1.cookie数据始终携带在同源的http请求中,即cookie在浏览器和服务器间来回传递,而sessionStorage和Localstorage不会自动把 ...

  5. Promise.all、race和any方法都是什么意思?

    // // 执行多个并行任务 const promiseAll = [ thenFs.readFile('./files/1.txt','utf8'), thenFs.readFile('./file ...

  6. [2024领航杯] Pwn方向题解 babyheap

    [2024领航杯] Pwn方向题解 babyheap 前言: 当然这个比赛我没有参加,是江苏省的一个比赛,附件是XiDP师傅在比赛结束之后发给我的,最近事情有点多,当时搁置了一天,昨天下午想起来这个事 ...

  7. 云原生周刊:2023 年 Java 开发人员可以学习的 25 大技术技能

    文章推荐 2023 年 Java 开发人员可以学习的 25 大技术技能 这篇文章为 Java 开发人员提供了 2023 年需要学习的一些重要技能,这些技能涵盖了现代 Java 开发.大数据和人工智能. ...

  8. 一起来看看大佬是怎样配置nginx虚拟主机

    在真实的服务器环境,为了充分利用服务器资源,一台nginx web服务器会同时配置N个虚拟主机,这样可以充分利用服务器的资源,方便管理员的统一管理 配置nginx虚拟主机首先肯定要先部署咱们的ngin ...

  9. 顶点着色网格转换为 UV 映射的纹理化网格

    简介 顶点着色是一种将颜色信息直接应用于网格顶点的简便方法.这种方式常用于生成式 3D 模型的构建,例如 InstantMesh.然而,大多数应用程序更偏好使用 UV 映射的纹理化网格. 本教程将介绍 ...

  10. 线性枚举(C语言)

    1.求最大值 比如求一个线性表中的最大值,可以先设定一个最大值,把它初始化为一个非常小的数,然后遍历给定的线性表,将其中的每个元素和目前的最大值比较,如果比它大,则更新这个最大值: 如果比它小,就不做 ...