介绍

JSR-303规范(Bean Validation规范)提供了对 Java EE 和 Java SE 中的 Java Bean 进行验证的方式。该规范主要使用注解的方式来实现对 Java Bean 的验证功能。

作用

前端传递数据到后端时,可以使用其对Bean对象的属性进行合法性校验。

快速开始

导入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Java Bean

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {@TableIdprivate Long id;@NotBlank(message = "用户名不能为空")@TableField("user_name")private String userName;@Size(max = 20,min = 6,message = "密码的长度必须在6-20位")@TableField("pass_word")private String passWord;@TableField("group_id")private Long groupId;@Max(value = 100, message = "城市编号不等大于100")@TableField("city_id")private Long cityId;@Email(message = "邮件不合法")@TableField("email")private String email;
}

统一返回结果

统一响应结果枚举类

@Getter
@AllArgsConstructor
@ToString
public enum ResponseEnum {SUCCESS(0, "成功"),ERROR(-1, "服务器内部错误"),private final Integer code;private final String message;
}

统一结果返回类

@Data
public class R {private Integer code;private String message;/*** 返回的数据*/private Map<String, Object> data = new HashMap<>();private R() {}public static R ok() {return setResult(ResponseEnum.SUCCESS);}public static R error() {return setResult(ResponseEnum.ERROR);}/*** 设置特定结果*/public static R setResult(ResponseEnum responseEnum) {R r = new R();r.setCode(responseEnum.getCode());r.setMessage(responseEnum.getMessage());return r;}/*** 设置响应消息*/public R message(String message) {this.setMessage(message);return this;}public R code(Integer code) {this.setCode(code);return this;}public R data(String key, Object value) {this.data.put(key, value);return this;}public R data(Map<String, Object> map) {this.setData(map);return this;}
}

方法一

Bean对象的下一个位置的参数写BindingResult对象,当JSR303校验失败后可以由BindResult对象捕获异常

controller层

@Valid注解后面的对象是要校验的Bean对象

Bean对象的下一个位置的参数写BindingResult对象

@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate UserServiceImpl userService;@PostMappingpublic R addUser(@Valid @RequestBody User user, BindingResult bindingResult) {Map<String, Object> map = new HashMap<>();// 判断是否有错误if (bindingResult.hasErrors()) {// 如果有错误,遍历错误信息,添加到Map中bindingResult.getFieldErrors().forEach((item) -> {// 获取错误提示String message = item.getDefaultMessage();// 获取错误的属性名称String field = item.getField();map.put(field, message);});return R.error().message("参数信息错误").data(map);}userService.save(user);return R.ok();}
}

方法二

当接口非常多,每一个接口都要写校验非常麻烦,写一个统一异常处理的类来集中处理异常

统一异常处理类

@Slf4j
@Component //Spring容易自动管理
@RestControllerAdvice //在controller层添加通知。当Controller层出现异常,这里的方法会捕获异常,返回错误信息(相当于服务降级)
public class UnifiedExceptionHandler {/*** 参数校验异常处理*/@ExceptionHandler(value = MethodArgumentNotValidException.class)public R handleValidException(MethodArgumentNotValidException e) {log.error("数据校验出现问题{}, 异常类型{}", e.getMessage(), e.getClass());BindingResult bindingResult = e.getBindingResult();Map<String, Object> map = new HashMap<>();bindingResult.getFieldErrors().forEach((item) -> {map.put(item.getField(), item.getDefaultMessage());});return R.error().message("参数信息错误").data(map);}
}

controller层

接口方法的参数如果有BindResult对象,代表校验出错由BindResult接受异常

接口方法的参数没有BindResult对象,代表校验出错将抛出异常,被统一异常处理类的方法接受

接口方法的参数没有BindResult对象,也没有统一异常处理类的方法接受,就抛出400的异常

@PostMapping
public R addUser(@Valid @RequestBody User user) {userService.save(user);return R.ok();
}

测试

校验成功的测试

请求体的JSON数据

{"userName": "lixianchichi","passWord": "104ee44","groupId": 1,"cityId": 14,"email": "123456@qq.com"
}

返回的响应信息

{"code": 0,"message": "成功","data": {}
}

校验失败的测试

请求体的JSON数据

{"userName": "lixianchichi","passWord": "10444eeeeeeeeeeeeeeeeee44","groupId": 1,"cityId": 1514,"email": "123456@qq.com"
}

返回的响应信息

{"code": -1,"message": "参数信息错误","data": {"passWord": "密码的长度必须在6-20位","cityId": "城市编号不等大于100"}
}

方法一与方法二测试结果相同,都为如上结果

分组校验

使用场景:不同情况下的校验规则是不同的,如新增的时候自动生成Id,所以数据不需要携带Id,而修改的时候必须要携带Id(不同场景触发不同的校验条件)

创建valid包

包里面创建两个空接口InsertGroup和UpdateGroup,代表新增和修改两种环境。

Java Bean

每一个Bean校验注解都有一个groups属性,值是一个接口字节码对象的数据,用来指定环境。

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {@TableId// 更新的时候校验@NotNull(message = "修改用户id不能为null" ,groups = {UpdateGroup.class})// 新增的时候校验@Null(message = "新增用户id必须为null" ,groups = {InsertGroup.class})private Long id;// 新增和修改的时候都校验@NotBlank(message = "用户名不能为空" ,groups = {InsertGroup.class, UpdateGroup.class})@TableField("user_name")private String userName;...// 修改的时候判断email是否合法,可以null,不报错(不传就不校验)@Email(message = "邮件不合法" ,groups = {UpdateGroup.class})@TableField("email")private String email;
}

controller层

使用@Validated代替@Valid注解,该注解可以指定环境(接口字节码对象),如下:代表当前是新增的情况

@PostMapping
public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) {userService.save(user);return R.ok();
}

注意:没有指定groups属性的注解,在controller层指定环境的情况下,不会生效

测试

在新增环境,前端传递JSON对象如果带Id属性

@PostMapping
public R addUser(@Validated({InsertGroup.class}) @RequestBody User user) {userService.save(user);return R.ok();
}

响应结果

{"code": -1,"message": "参数信息错误","data": {"id": "新增用户id必须为null"}
}

在修改环境,前端传递JSON对象如果带Id属性

@PostMapping
public R addUser(@Validated({UpdateGroup.class}) @RequestBody User user) {userService.save(user);return R.ok();
}

响应结果

{"code": -1,"message": "参数信息错误","data": {"id": "修改用户id不能为null"}
}

自定义校验注解

实现功能:校验Bean的某个属性的字段只能是0和1

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User {...@TableField("group_id")@ListValue(vals = {0L, 1L}, groups = {UpdateGroup.class})private Long groupId;...
}

自定义的校验注解

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 指定校验器,可以指定多个校验器,会自动适配,如:此注解还有一个Double数组的属性,再添加一个Double类型的校验器,当我们使用注解的时候,我们给Double数组赋值,就会自动找Double类型的校验器校验
@Constraint(validatedBy = {ListValueCondtraintValidator.class})
public @interface ListValue {// 前三个属性都是每个JSR303注解必须有的// 默认错误信息,从properties里获取值String message() default "{com.lixianhe.valid.listValue.message}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};long[] vals() default {};
}

自定义校验器

/*** 自定义校验器*/
public class ListValueCondtraintValidator implements ConstraintValidator<ListValue, Long> {private Set<Long> set = new HashSet<>();/*** 初始化方法** @param constraintAnnotation*/@Overridepublic void initialize(ListValue constraintAnnotation) {long[] vals = constraintAnnotation.vals();for (long val : vals) {set.add(val);}}/*** 判断是否校验成功** @param value                      需要校验的值* @param constraintValidatorContext* @return 校验结果*/@Overridepublic boolean isValid(Long value, ConstraintValidatorContext constraintValidatorContext) {return set.contains(value);}
}

测试

当groupId传2的时候

{"userName": "lixianchichi","passWord": "104eefd454545546456565tygpl[per44","groupId": 2,"cityId": 10
}

响应结果

{"code": -1,"message": "参数信息错误","data": {"groupId": "必须提交指定的值"}
}

补充

Java Bean校验注解总结

限制 说明
@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
@URL 校验是否位合法的URL

JSR303校验前端传递的数据相关推荐

  1. MVC框架中的前端与后端数据传递及实例

    一.MVC框架 MVC代表Model.View.Controller,即模型.视图.控制器.其中: View(视图):就是人机交互界面,可以给用户显示业务逻辑数据,同时也可以 接收用户输入的数据. M ...

  2. 前端给后端传递数据的时候,有些后端自己可以获取到的值应该由前端传递吗?

    前端给后端传递数据的时候,有些后端自己可以获取到的值应该由前端传递吗? 场景: 1.比如我向后端传递一个学生做的试卷. 2.后端需要我把学生的答案和标准答案(在获取试卷的时候给我的,我知道很扯淡)同时 ...

  3. MVC中利用ViewBag传递Json数据时的前端处理方法

    ** MVC中利用ViewBag传递Json数据时的前端处理方法 ** 用viewBag传递Json字符串到前端时,json字符串中的"会被转义为& quot,前端处理方法为@Htm ...

  4. JSR303校验的简单使用以及自定义校验规则的代码编写

    文章目录 一.JSR303校验 1.简介 2.相关注解 3.JSR303依赖包 二.JSR303自带的校验规则 1.在JavaBean上添加校验规则 2.生效校验规则 2.1 controller返回 ...

  5. @RequestBody注解失效?从前端传来的数据到底需不需要@RequestBody注解?前端传输数据解析的问题?

    @RequestBody注解失效?从前端传来的数据到底需不需要@RequestBody注解? 又是在日常的从Mybatis升级Mybatis-Plus的过程中,我又发现了问题:怎么从前端传来的数据没有 ...

  6. SpringMVC(二)——转发和重定向、处理前端请求的数据(普通字符串/对象)

    文章目录 1. 转发和重定向 2. 处理前端请求的数据 2.1 普通字符串 2.2 对象 1. 转发和重定向 转发:url不会发生变化 (查询前端固定模板的数据) @RequestMapping(&q ...

  7. 前后端分离项目,后端是如何处理前端传递的token?

    前后端分离项目中,在不使用 SpringSecurity.Shiro 安全框架的情况下,后端是如何处理前段传递的 token 的呢? 简单说一个场景,在一个非常小的项目中,由于业务逻辑比较简单,也没有 ...

  8. 的write方法有哪些参数_向子进程传递大量数据的方法

    如何传递大型数据给子进程 昨天的一篇文章中,我们说到如果想向一个子进程传输多于32767个字符的数据,我们需要寻找其他的方法(而不是命令行参数)来实现. 我们能想到的第一个方法是:WM_COPYDAT ...

  9. 前端转行大数据?没必要

    文/北妈 阅读本文需要 4.5分钟 一最近又有读者,问我要不要转去学大数据,好像前端和大数据一点不沾边.... 说实话我是无语的 文长,需耐心看完,读时有耐心,看完有信心. 这几年大数据和机器学习一直 ...

最新文章

  1. vs开发工具报错:参数错误 异常来自 HRESULT:0x80070057 E_INVALIDARG
  2. 一个不错的安全评估站点vulnerabilityassessment.co.uk
  3. Matlab篇(三)MATLAB中conj的用法
  4. github emoji 表情列表
  5. C指针原理(25)-gtk
  6. 看完c++ primer之后看什么
  7. 分子结构模拟工具UCSF Chimera的安装及基本操作
  8. 街机三国服务器维护,街机三国4月2日07:00更新维护公告
  9. 今后,去踢“大数据足球”
  10. SharePoint 2010 文档管理系列
  11. 蓝桥杯 平方怪圈 JAVA
  12. DoIP协议:通用DoIP首部否定确认码02和03的区别
  13. 如何理解熵、交叉熵、KL散度、JS散度
  14. Cadence输出Gerber文件
  15. 计算机网络提出问题,南昌大学高级计算机网络课堂提问及详解.doc
  16. unit系统与linux系统区别,python+unittet在linux与windows使用的区别
  17. C#三打一网络扑克游戏软件开发(一)
  18. MER 音乐情感识别-论文笔记8
  19. 如何关闭伽卡他卡的开机自启
  20. 人脸识别会被留底吗_人脸识别会保存我们的照片吗?

热门文章

  1. dw网页插入java小程序_DW网页设计100例35:构建Java插入模块
  2. ChatGPT为什么会一本正经胡说八道?我们如何改进它?| 文内附有代码
  3. 抢占Smart Finance创世节点,激活Gamefi机枪池
  4. Poj 1094 拓扑排序Kahn
  5. 360安全卫士、浏览器广告及杂碎讨厌功能屏蔽
  6. java通过poi生成excel表格(自适应列宽、合并单元格后的边框添加)
  7. 2020年湖北省冬小麦种植分布数据
  8. STM32的RFID智能门禁系统设计
  9. 因果效应与uplift模型
  10. ADOLISP在64位系统上读取数据库的解决办法