首页 > 其他分享 >JSR303统一校验

JSR303统一校验

时间:2024-11-30 09:29:17浏览次数:9  
标签:groups 自定义 JSR303 校验 AddGroup 注解 class 统一

1、简介

jsr 是 Java Specification Requests 的缩写,意思是java的请求规范。周志明老师的书上还着重介绍过jsr292(jvm多语言支持包括Kotlin,Clojure,JRuby,Scala等)。

JSR303着重参数校验功能,点开javax.validation.constraints,可以看到已经封装好的注解有这些:

使用jsr303规范很简单,第一步在实体类相应字段上标注校验注解,比如@Email或者标注自定义校验注解@Pattern(regexp=”“)自定义正则表达式来处理;第二步是使用校验,只需要在@RequestBody之前加上@Valid注解即表明开启校验。

2、分组校验

更复杂的场景,我们可以分组校验:

​ 1)、 @NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})​ 给校验注解标注什么情况需要进行校验,比如增加不校验修改时校验。这里group里传入的是个接口。​ 2)、开启分组校验要使用spring实现的注解@Validated({AddGroup.class})​ 3)、默认没有指定分组的校验注解比如@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效,也就是说Validated后加了分组那么不加分组的校验注解就会失效;

细节如下

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;
 
    /**
     * 品牌id
     */
    @NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
    @Null(message = "新增不能指定id", groups = {AddGroup.class})
    @TableId
    private Long brandId;
    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
    private String name;
    /**
     * 品牌logo地址
     */
    @NotBlank(groups = {AddGroup.class})
    @URL(message = "logo必须是一个合法的url地址", groups = {AddGroup.class, UpdateGroup.class})
    private String logo;
    /**
     * 介绍
     */
    private String descript;
    /**
     * 显示状态[0-不显示;1-显示]
     */
//  @Pattern()
    @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
    @ListValue(values = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
    private Integer showStatus;
    /**
     * 检索首字母
     */
    @NotEmpty(groups = {AddGroup.class})
    @Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母", groups = {AddGroup.class, UpdateGroup.class})
    private String firstLetter;
    /**
     * 排序
     */
    @NotNull(groups = {AddGroup.class})
    @Min(value = 0, message = "排序必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
    private Integer sort;
}

controller层内容:

public R update(@Validated(UpdateGroup.class) @RequestBody BrandEntity brand)

 

3、自定义校验注解

还有自定义校验,可以编写一个自定义的校验注解,然后编写一个自定义的校验器 ConstraintValidator,然后两者关联。

也就是说@Pattern()正则不能满足校验的情况,可以使用自定义校验注解。

比如对showStatus做自定义校验,规定只能是整数0或1。

需要先按照规范自定义一个注解@ListValue()

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
 
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{com.flitsneak.common.valid.ListValue.message}";
 
    Class<?>[] groups() default {};
 
    Class<? extends Payload>[] payload() default {};
 
    int[] values() default {};
}

注解增加属性int[],提示信息仿照规范自定义common模块resource目录下新建

ValidationMessages.properties文件

com.flitsneak.common.valid.ListValue.message=必须提交指定的值

validatedBy后传入我们自定义的校验器,注解作为参数通过自定义校验器ListValueConstraintValidator对参数进行校验。

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;
 
/**
 * @Author FlitSneak
 * @Date 2021/6/24
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    private final Set<Integer> set = new HashSet<>();
    /**
     * 初始化方法
     * 参数:自定义注解的详细信息
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] values = constraintAnnotation.values();
        for (int val : values) {
            set.add(val);
        }
 
    }
 
    /**
     * 判断是否校验成功
     *
     * @param value   需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}

就是将注解的值放大set集合之中,然后对字段之判断是否在set集合之中。

统一拦截处理

校验响应的结果可以用BindingResult来接收处理返回,但是相当麻烦,推荐做统一拦截处理。

BingdingResult处理方式:

public R save(@Valid @RequestBody BrandEntity brand, BindingResult bindingResult){
        if (bindingResult.hasErrors()){
            Map<String,String> map = new HashMap<>();
            //获取校验错误结果
            bindingResult.getFieldErrors().forEach(i->{
                //获取到错误提示
                String message = i.getField();
                //获取出错的字段
                String field = i.getField();
                map.put(field,message);
            });
            return R.error(400,"校验错误").put("data",map);
        }else {
 
        brandService.save(brand);
 
        return R.ok();}
    }

改为ControllerAdvice统一拦截处理,

@Slf4j
@RestControllerAdvice(basePackages = "com.flitsneak.mall.product.controller")
public class MallControllerAdvice {
    @ExceptionHandler(value= MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();
 
        Map<String,String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError)->{
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(),BizCodeEnum.VALID_EXCEPTION.getMsg()).put("data",errorMap);
    }
 
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){
 
        log.error("错误:",throwable);
        return R.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(),BizCodeEnum.UNKNOWN_EXCEPTION.getMsg());
    }
}

指定拦截的是MethodArgumentNotValidException异常,异常对象e里面可以获取BindingResult,处理方式一样。

补充

前端和后端都应该对参数做检验屏蔽非法请求,jsr303很多企业并没有应用,仍然是 使用CollectionUtil或者StringUtil进行处理。

我们项目用的springboot版本是2.3.x,而2.3.x以上剥离了jsr303,所以要使用注解校验,需要导入一下两个包:

<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.3.4.RELEASE</version>
</dependency>

spring-boot-starter-validation下的version字段和springboot版本对应,本项目springboot版本是2.3.4.RELEASE

标签:groups,自定义,JSR303,校验,AddGroup,注解,class,统一
From: https://blog.csdn.net/nonagontech/article/details/144149688

相关文章

  • [React]antd表单校验函数写法
    来自文心一言通过 rules 属性来定义校验规则,其中可以包含自定义的校验函数 validatorimportReactfrom'react';import{Form,Input,Button}from'antd';constMyForm=()=>{const[form]=Form.useForm();//自定义校验函数constcheckUsername=(......
  • 你有使用:valid和:invalid来校验表单吗?
    是的,我知道在前端开发中,:valid和:invalid是CSS伪类选择器,用于根据表单元素的有效性状态来设置样式。它们与HTML5的表单验证API一起使用。具体来说::valid:当表单元素的值符合其定义的验证规则(例如,required、type="email"、pattern等)时,该伪类选择器将应用样式。:invalid:......
  • 华为OD机试真题-ai面板识别-2024年OD统一考试(E卷)
    最新华为OD机试考点合集:华为OD机试2024年真题题库(E卷+D卷+C卷)_华为od机试题库-CSDN博客     每一题都含有详细的解题思路和代码注释,精编c++、JAVA、Python三种语言解法。帮助每一位考生轻松、高效刷题。订阅后永久可看,发现新题及时跟新。题目描述AI识别到面板上有N(1......
  • Day01_黑马甄选统一数仓介绍_FlinkCDC
    文章目录Day01_随堂笔记一、经典数仓架构二、传统离线大数据架构三、lambda架构Kafka是什么(可略)核心概念特性应用场景参考文档四、kappa架构五、混合架构六、传统数据入仓特点七、实时数仓现状八、湖仓一体九、统一数仓十、CDC(ChangeDataCapture:变更_数据_捕获)......
  • 【架构】统一认证中心的架构设计与实现(一)
    一、背景二、认证授权概念2.1认证2.2授权三、一些名词四、常见的认证方案4.1JWT4.2OAuth2五、SSO协议5.1OpenIDConnect5.2SAML2.05.3CAS六、RBAC一、背景一般在一个公司内部可能有多个系统,比如OA,财务系统,CMS,ERP,营销系统......
  • 代码随想录算法训练营第十四天(统一迭代;LeetCode226.翻转二叉树;LeetCode101.对称二叉树
    统一迭代LeetCode144.二叉树的前序遍历题目链接:二叉树的前序遍历题目链接代码/***Definitionforabinarytreenode.*publicclassTreeNode{*intval;*TreeNodeleft;*TreeNoderight;*TreeNode(){}*TreeNode(intval)......
  • QtCRC校验
    1crc校验子程序思路:取出待校验字节的最后两个字节,然后与计算出的crc进行比较,如果相同,则crc校验正确。说明:这种方法必须确保自己计算的是正确的,不然哪怕人家发过来的是对的,也会校验失败注意:一定要注意crc校验码的高低字节!!!使用该子程序时传入的数据格式为可以使用QByteArray::......
  • 华为OD机试E卷 --最大报酬 --24年OD统一考试(Java & JS & Python & C & C++)
    文章目录题目描述输入描述输出描述用例题目解析JS算法源码Java算法源码python算法源码c算法源码c++算法源码题目描述小明每周上班都会拿到自己的工作清单,工作清单内包含n项工作,每项工作都有对应的耗时时间(单位h)和报酬,工作的总报酬为所有已完成工......
  • 华为OD机试E卷 --绘图机器人--24年OD统一考试(Java & JS & Python & C & C++)
    文章目录题目描述输入描述输出描述用例题目解析js算法源码Java算法源码python算法源码c算法源码c++算法源码题目描述绘图机器的绘图笔初始位置在原点(0,0)机器启动后按照以下规则来进行绘制直线.尝试沿着横线坐标正向绘制直线直到给定的终点E期间......
  • element表单校验validate的封装
    /***V2*@paramvm$refs.formName*@returns{boolean}*/exportfunctionformValidate(vm){vm.validate();letvalidateList=vm.fields.map(_vm=>({validateState:_vm.validateState,validateMessage:_vm.validateMessage}));if(v......