数据校验JSR303快速入门(简单使用、分组效验、自定义注解效验)

您所在的位置:网站首页 分组词什么 数据校验JSR303快速入门(简单使用、分组效验、自定义注解效验)

数据校验JSR303快速入门(简单使用、分组效验、自定义注解效验)

2024-07-10 04:04:39| 来源: 网络整理| 查看: 265

前言: 在实际开发中,除了前端需要在表单中验证用户的输入。后台服务也需要对用户传入的参数进行效验,避免他人在得知请求格式后,直接通过类似Postman这样的测试工具进行非常数据请求。

JSR303是什么

JSR303是一套JavaBean参数校验的标准,定义了很多常用的校验注解 可以直接将这些注解加在我们JavaBean的属性上面就可以在需要校验的时候进行校验了

接下来以添加品牌为例

简单的数据效验

我们可以使用javax.validation.constraints包中提供的注解,给实体类字段添加规则。 在这里插入图片描述

1、给实体类添加验证规则 @Data @TableName("pms_brand") public class BrandEntity implements Serializable { private static final long serialVersionUID = 1L; @TableId private Long brandId; //约束name不能为null,且至少有一个非空字符 @NotBlank(message = "品牌名必须提交") private String name; @NotBlank(message = "logo不能为空") //URL是hibernate提供的注解,实现了JSR303规范。约束如果logo不为null的话,必须符合url格式 @URL(message = "logo格式不符") private String logo; private String descript; // @Pattern(regexp = "[0-1]") //pattern不支持Integer private Integer showStatus; //使用正则表达式约束字段 @Pattern(regexp = "^[a-zA-Z]$" , message = "首字母必须是一个字母") private String firstLetter; @Min(value = 0 , message = "排序字段必须大于等于0") private Integer sort; } 2、controller层使用@Valid开启验证功能 @RequestMapping("/save") public R save(@Valid @RequestBody BrandEntity brand){ brandService.save(brand); return R.ok(); } 3、使用postman测试验证

在这里插入图片描述 至此,已经完成了最简单的后台数据效验。步骤很简单:

使用注解对实体类字段进行约束使用@Valid开启数据验证功能 使用BindingResult处理异常

在上例中,虽然我们完成了数据验证,但是返回给前端的数据并不友好。在项目中应该返回一个统一的结果。

使用BindingResult接受参数效验的结果

@RequestMapping("/save") //@RequiresPermissions("product:brand:save") public R save(@Valid @RequestBody BrandEntity brand, BindingResult bindingResult) { if (bindingResult.hasErrors()) { //1.出现参数非法情况 Map map = new HashMap(); bindingResult.getFieldErrors().forEach(fieldError -> { map.put(fieldError.getField(), fieldError.getDefaultMessage()); }); return R.error(400, "提交的数据不合法").put("data", map); } else { //2.参数验证通过, 执行正常逻辑 brandService.save(brand); return R.ok(); } }

再次测试: 在这里插入图片描述 非常nice

统一异常处理

添加BindingResult参数后,虽然可以使后台在出现异常时,进行处理并返回统一的结果。但是我们会发现,我们写了许多与业务不相关的代码,为了解决这个问题,我们可以通过@ControllerAdvice进行异常的统一处理。

1、编写统一异常处理类

//@RestControllerAdvice其实就是@ControllerAdvice+@ResponseBody,表示返回的是json格式 @RestControllerAdvice @Slf4j public class GlobalExceptionControllerAdvice { //捕获MethodArgumentNotValidException类型的异常 @ExceptionHandler(MethodArgumentNotValidException.class) public R handlerMethodArgumentNotValidException(MethodArgumentNotValidException e){ BindingResult bindingResult = e.getBindingResult(); Map map = new HashMap(); bindingResult.getFieldErrors().forEach(fieldError -> { map.put(fieldError.getField(), fieldError.getDefaultMessage()); }); return R.error(400,"提交的数据不合法").put("data",map); } //兜底 @ExceptionHandler(Exception.class) public R handlerException(Exception e){ return R.error(10000,"未知的系统异常").put("data",e.getMessage()); } }

2、把之前加的BindingResult去掉,还原成原先最干净的代码

@RequestMapping("/save") public R save(@Valid @RequestBody BrandEntity brand) { brandService.save(brand); return R.ok(); }

测试结果一样的。

code状态码

在我们后台的返回结果中,有个code状态码,前端可以根据这个状态码判断是出现了什么问题,如何解决。就好像是我们进行http请求时,我们知道200响应码代表请求成功、404代表找不到资源、500代表服务器出错等。

随着业务越来越复杂,异常的类型越来越多,为了统一规范,我们就不该将code状态码写死在代码中,而应该统一管理起来。

我们可以使用枚举类进行管理,如下。

/*** * 错误码和错误信息定义类 * 1. 错误码定义规则为5为数字 * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常 * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式 * 错误码列表: * 10: 通用 * 001:参数格式校验 * 11: 商品 * 12: 订单 * 13: 购物车 * 14: 物流 */ public enum BizCodeEnume { UNKNOW_EXCEPTION(10000,"系统未知异常"), VAILD_EXCEPTION(10001,"参数格式校验失败"); private int code; private String msg; BizCodeEnume(int code,String msg){ this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } }

2、修改我们全局异常处理类中的代码

@RestControllerAdvice @Slf4j public class GlobalExceptionControllerAdvice { //捕获MethodArgumentNotValidException类型的异常 @ExceptionHandler(MethodArgumentNotValidException.class) public R handlerMethodArgumentNotValidException(MethodArgumentNotValidException e){ BindingResult bindingResult = e.getBindingResult(); Map map = new HashMap(); bindingResult.getFieldErrors().forEach(fieldError -> { map.put(fieldError.getField(), fieldError.getDefaultMessage()); }); return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",map); } //兜底 @ExceptionHandler(Exception.class) public R handlerException(Exception e){ return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg()).put("data",e.getMessage()); } } 分组效验

在简单的数据验证中,我们使用完成了数据验证。但是还存在一些问题,如在添加品牌的时候brandId为null,但在修改品牌的时候brandId不能为null,这样的话,就冲突了。

那怎么办呢?我们可以给他们分个组,添加操作使用一组验证规则,修改操作使用一组验证规则。这就是分组验证的功能。

以@NotNull注解为例

@Constraint(validatedBy = { }) public @interface NotNull { String message() default "{javax.validation.constraints.NotNull.message}"; //分组验证时使用 Class[] groups() default { }; ...

我们通过@NotNul注解的groups指定属于哪个组

实现步骤:

1、创建AddGroup和UpdateGroup接口分别表示添加组和更新组

//这俩个接口只是用来标记的,不需要实现 public interface AddGroup { } public interface UpdateGroup { }

2、实体类中使用注解时,标明该验证规则属于哪个组

@Data @TableName("pms_brand") public class BrandEntity implements Serializable { private static final long serialVersionUID = 1L; @TableId //只有在AddGroup组才会生效 @Null(message = "添加操作 不要传brandId",groups = AddGroup.class) //只有在UpdateGroup组才会生效 @NotNull(message = "修改操作 brandId不能为null",groups = UpdateGroup.class) private Long brandId; @NotBlank(message = "添加操作 品牌名必须提交",groups = AddGroup.class) private String name; @NotBlank(message = "添加操作 logo不能为空",groups = AddGroup.class) //在AddGroup组和UpdateGroup组中都会生效 @URL(message = "logo格式不符",groups = {AddGroup.class,UpdateGroup.class}) // private String logo; private String descript; /** * 显示状态[0-不显示;1-显示] */ // @Pattern(regexp = "[0-1]") //pattern不支持Integer private Integer showStatus; @Pattern(regexp = "^[a-zA-Z]$" , message = "首字母必须是一个字母" , groups = {AddGroup.class,UpdateGroup.class}) private String firstLetter; @Min(value = 0 , message = "排序字段必须大于等于0",groups = {AddGroup.class,UpdateGroup.class}) private Integer sort; }

3、使用@Validated替代@Valid

@Validated是@Valid的变体,它支持分组效验功能

@RequestMapping("/save") //使用AddGroup组中的验证规则 public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand) { brandService.save(brand); return R.ok(); } @PutMapping("/update") //使用UpdateGroup组中的验证规则 public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand) { brandService.updateById(brand); return R.ok(); }

4、测试

测试添加操作: 在这里插入图片描述 测试修改操作: 在这里插入图片描述 至此分组验证功能完成

自定义效验注解 /** * 显示状态[0-不显示;1-显示] */ // @Pattern(regexp = "[0-1]") //pattern不支持Integer private Integer showStatus;

在@Pattern的注释中,有下面这一段话,说明了该注解不支持Interger类型。那怎么办呢?

Accepts {@code CharSequence}. {@code null} elements are considered valid.

当提供的验证规则中没有我们需要的时,它支持我们自定义验证规则。(我知道有办法实现只能0和1,我只是想说可以自定义效验注解,别杠( o=^•ェ•)o ┏━┓)

自定义效验注解步骤:

添加依赖编写一个自定义的效验注解编写一个自定义的效验器关联自定义的效验器和自定义的效验注解

1、添加依赖

javax.validation validation-api 2.0.1.Final

2、编写一个自定义的效验注解

该注解的功能,验证输入的参数是否在value中。

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented //指定使用哪个效验器,如果不指定的话,就需要在初始化的时候指定 //可以指定多个不同的效验器,适配不同类型的效验 @Constraint(validatedBy = { ListValueConstraintValidator.class}) public @interface ListValue { //JSR303规范中,要求必须有message、groups、payload这三个方法 //default: 当message为null时,默认会到ValidationMessages.properties配置文件中找com.fcp.common.valid.ListValue.message的值 String message() default "{com.fcp.common.valid.ListValue.message}"; Class[] groups() default { }; Class[] payload() default { }; //用来存放符合规则的数字 int[] value(); }

3、在工程resource中创建ValidationMessages.properties配置文件

com.fcp.common.valid.ListValue.message=The committed number is not in the specified array

4、编写一个自定义的效验器

/** * ListValue:使用的效验注解类型 * Integer: 被验证目标类型。我们验证的目标都是数字所以是Integer */ public class ListValueConstraintValidator implements ConstraintValidator { private Set contain = new HashSet(); @Override public void initialize(ListValue constraintAnnotation) { int[] values = constraintAnnotation.value(); if (values==null) return; //将符合规则的值放到容器中 for (int value : values) { contain.add(value); } } //该方法判断参数合不合法 //value是需要验证的值,即用户输入的参数 @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { //返回用户输入的参数是否在容器中 return contain.contains(value); } }

5、关联自定义的效验器和自定义的效验注解 第二步已经做了,就是这个

@Constraint(validatedBy = { ListValueConstraintValidator.class})

至此,自定义效验器完成,可以开心的使用了:

//表示输入的参数,必须要在value指定的数组中,也就是0和1 @ListValue(value = {0, 1},groups = {AddGroup.class,UpdateGroup.class}) private Integer showStatus;

测试: 在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭