一、概述

在 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=) 被注解元素必须符合指定的正则表达式
  1. Hibernate Validator 扩展的注解
注解名称 说明
@NotBlank(message=) 被注解的字符串必须非 null 且trim()后长度大于 0
@Email 被注解元素必须是电子邮箱地址
@Length(min=,max=) 被注解的字符串的长度必须在指定范围内
@NotEmpty 被注解元素(字符串、数组、集合等)必须非 null 且长度大于 0
@Range(min=,max=,message=) 被注解元素必须在合适的范围内
@URL 被注解元素必须是合法的 URL

三、用法

  1. 依赖

如果你使用的是 SpringBoot(spirngboot升级到2.3之后默认不再包含),那么只需引入spring-boot-starter-web即可,它的子依赖中包含了hibernate-validator和必要的数据绑定组件

在pom.xml中引入 Hibernate Validator 需要的依赖包:
<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>6.0.10.Final</version>
</dependency>

springboot2.3后,使用以下:

  <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
  1. 实体类
@Data
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;
}

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

  1. 处理器方法

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

@Controller
public class StudentController {@AutowiredStudentMapper studentMapper;@PostMapping("/student")@ResponseBodypublic String addStudent(@RequestBody @Validated Student student, BindingResult result) {if (result.hasErrors()) {return JSONObject.toJSONString(result.getAllErrors());}studentMapper.insert(student);return "success";}
}

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

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

控制台输出:

四、自定义校验

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

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

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

  1. 校验者类
    实现ConstraintValidator接口:
//第一个泛型参数是表明校验的注解类型,第二个泛型参数是需要被校验的类型
class NotContainSpaceValidator implements ConstraintValidator<NotContainSpace, String> {private String attr;/*** 初始化事件方法* @param notContainSpace*/@Overridepublic void initialize(NotContainSpace notContainSpace) {attr=notContainSpace.attr();}/*** 判断是否合法的方法* @param s* @param context* @return*/@Overridepublic boolean isValid(String s, ConstraintValidatorContext context) {if (s != null && s.trim().contains(" ")) {// 获取默认提示信息String constraintMessageTemplate = context.getDefaultConstraintMessageTemplate();System.out.println(attr+constraintMessageTemplate);// 禁用默认提示信息// context.disableDefaultConstraintViolation();// 设置自定义信息context.buildConstraintViolationWithTemplate(attr+" 不能包含空格").addConstraintViolation();return false;}return true;}
}

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

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

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

  1. 使用
@NotContainSpace(attr="name")
private String name;

发送数据:

控制台输出:

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

五、分组校验

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

public class Student {@Min(value = 1, groups = {Update.class}, message = "id错误")private Long id;  // 只有在Update分组下,限制才会起作用public interface Update{} // 分组是接口形式// ...
}

我们上面在对方法参数添加自动校验时用的是@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 的区别
参考文章

Spring Validation 校验相关推荐

  1. Spring Validation校验

    本文来说下现在使用的比较多的Spring Validation校验框架. 文章目录 概述 校验注解 JSR-303包含的注解 Hibernate Validator扩展的注解 简单使用 引入依赖 re ...

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

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:六点半起床 juejin.im/post/685654110 ...

  3. spring全局异常抓取validation校验信息

    spring全局异常抓取validation校验信息 参考文章: (1)spring全局异常抓取validation校验信息 (2)https://www.cnblogs.com/jianxiaopo ...

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

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

  5. js 强校验 弱校验_还在手写表单校验逻辑?试试spring validation吧

    Java识堂,一个高原创,高收藏,有干货的微信公众号,欢迎关注 前言 数据的校验是交互式网站一个不可或缺的功能,前端的js校验可以涵盖大部分的校验职责,如用户名唯一性,生日格式,邮箱格式校验等等常用的 ...

  6. 使用 Spring Validation 优雅地进行参数校验

    引言 不知道大家平时的业务开发过程中 controller 层的参数校验都是怎么写的?是否也存在下面这样的直接判断? public String add(UserVO userVO) {if(user ...

  7. Spring Validation的校验顺序问题解决方案(建议读到最后,有大坑)

    Spring Validation的校验顺序问题解决方案 问题场景 原理剖析 解决方法 创建五个接口 修改Controller控制层代码 修改实体类代码 整改结果 后续问题 问题原因 解决方案 问题场 ...

  8. Spring Validation实现原理,参数校验没那么简单

    前言 本文会详细介绍Spring Validation各种场景下的最佳实践及其实现原理,死磕到底! 项目源码:spring-validation 简单使用 Java API规范(JSR303)定义了B ...

  9. 【Spring】掌握 Spring Validation 数据校验

    个人简介:Java领域新星创作者:阿里云技术博主.星级博主.专家博主:正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ Spring Validati ...

最新文章

  1. PAT L2-014. 列车调度
  2. [THUWC2017]在美妙的数学王国中畅游
  3. python画相关性可视化图上三角_完成这50个Matplotlib代码,你也能画出优秀的图表...
  4. 微信小程序云开发如何--实现简单的增删改查
  5. strip and linux lib compile
  6. 【原创】简单轻松浏览FTP
  7. 官方晒荣耀20系列旗舰预告片:搭载业界最大F/1.4光圈
  8. 金融银行业机器学习—机遇、风险、案例
  9. 自媒体图文、视频素材网站哪里找?这些非常好用
  10. 社交仅发送图片和视频 -设计测试用例
  11. SEO原创和伪原创文章的一些技巧
  12. 港科资讯 | 香港科大委任郭毅可教授为首席副校长
  13. ubuntu 16.04 无法连接wifi
  14. 计算机网络知识点整理---应用层(六)
  15. 二维高斯核函数(python)
  16. 中文信息处理实验2——基于词表的分词
  17. python中字符串转成数字的几种方法
  18. 学习方法——教中学、做中学、创中学
  19. 2018CCPC吉林赛区 题解
  20. week9:个人博客作业

热门文章

  1. Golang的数组、切片、映射的理解
  2. 爬虫---Beautiful Soup 通过添加不同的IP请求
  3. PHP保留小数的相关方法
  4. BZOJ5102 POI2018Prawnicy(堆)
  5. Web Services简单介绍
  6. Java Web 高性能开发,前端的高性能
  7. 全新的互动广告牌,待遇男女有别
  8. c++学习笔记之静态成员函数
  9. 邵阳学院计算机科学与技术专业分最低,邵阳学院在湖南各专业录取最低分/最低位次...
  10. 5 Android数据存储 任务二 应用程序数据文件夹里的文件读写 ,