Java利用自定义注解实现数据校验

目录
  • JSR303介绍
    • 引入依赖
    • 常用注解
    • 开启校验
  • 数据校验测试
    • 自定义的封装错误信息
    • 统一异常处理
  • 分组校验
    • 创建分组校验接口
    • 添加校验注解
    • 开启分组校验
  • 自定义校验
    • 编写自定义的校验注解
    • 编写自定义的校验器
    • 关联校验器和校验注解
    • 添加自定义的校验注解

JSR303介绍

在Java中提供了一系列的校验方式

这些校验方式在javax.validation.constraints包中

引入依赖

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

常用注解

@Null 验证对象是否为null

@NotNull 验证对象是否不为null, 无法查检长度为0的字符串

@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.

@NotEmpty 检查约束元素是否为NULL或者是EMPTY.

Booelan检查

  • @AssertTrue 验证 Boolean 对象是否为 true
  • @AssertFalse 验证 Boolean 对象是否为 false

长度检查

  • @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
  • @Length(min=, max=) Validates that the annotated string is between min and max included.

日期检查

  • @Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
  • @Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期
  • @Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。

数值检查

建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为”“,Integer为null

  • @Min 验证 Number 和 String 对象是否大等于指定的值
  • @Max 验证 Number 和 String 对象是否小等于指定的值
  • @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
  • @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
  • @Digits 验证 Number 和 String 的构成是否合法
  • @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
  • @Range(min=, max=) 被指定的元素必须在合适的范围内
  • @Range(min=10000,max=50000,message=”range.bean.wage”)
  • @Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
  • @CreditCardNumber信用卡验证
  • @Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
  • @ScriptAssert(lang= ,script=, alias=)
  • @URL(protocol=,host=, port=,regexp=, flags=)

开启校验

controller中加校验注解@Valid,开启校验

数据校验测试

步骤1:实体类字段上使用校验注解 @NotNull @NotEmpty @NotBlank @Pattern

步骤2:controller中加校验注解@Valid,开启校验

步骤3:给校验的Bean后,紧跟一个BindingResult,就可以获取到校验的结果

public R save(@Valid @RequestBody User user, BindingResult result){}

实体中添加注解

@Data
public class Student {
    @NotEmpty(message ="姓名不能为空")
    private String name;
}

controller层中保存方法添加:@Valid

   @PostMapping("/jsr")
    public AjaxResult testJrs(@Valid @RequestBody User user, BindingResult result) {
        String name = user.getName();
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("errors", result.getFieldErrors());
        return AjaxResult.success("数据校验", map);
    }

数据校验测试:测试:http://localhost:8080/test/jsr

@Data
public class User {

    @NotEmpty(message = "姓名不能为空")
    @ApiModelProperty("姓名")
    private String name;

    @ApiModelProperty("学号")
    private String id;

    @ApiModelProperty("年龄")
    private String age;
}

返回信息

{
  "msg": "数据校验",
  "code": 200,
  "data": {
    "name": "",
    "errors": [
      {
        "codes": [
          "NotEmpty.user.name",
          "NotEmpty.name",
          "NotEmpty.java.lang.String",
          "NotEmpty"
        ],
        "arguments": [
          {
            "codes": [
              "user.name",
              "name"
            ],
            "defaultMessage": "name",
            "code": "name"
          }
        ],
        "defaultMessage": "姓名不能为空",
        "objectName": "user",
        "field": "name",
        "rejectedValue": "",
        "bindingFailure": false,
        "code": "NotEmpty"
      }
    ]
  }
}

自定义的封装错误信息

    @PostMapping("/package")
    public AjaxResult testPackage(@Valid @RequestBody User user, BindingResult result) {
        String name = user.getName();
        Map<String, String> map = new HashMap<>();
        map.put("name", name);
        if (result.hasErrors()) {
            //1.获取错误的校验结果
            result.getFieldErrors().forEach((item) -> {
                //2.获取发生错误时的message
                String message = item.getDefaultMessage();
                //3.获取发生错误的字段
                String field = item.getField();
                map.put(field, message);
            });
            return AjaxResult.error("数据校验", map);
        } else {
            return AjaxResult.success(map);
        }
    }

自定义的封装错误信息:测试:http://localhost:80/test/package

{
  "name": "",
  "id": "demoData",
  "age": "demoData"
}

错误信息

{
  "msg": "数据校验",
  "code": 500,
  "data": {
    "name": "姓名不能为空"
  }
}

统一异常处理

@Slf4j
@RestControllerAdvice(basePackages = "com.michale.jrs303.controllers")
public class FireflyMallExceptionControllerAdvice {
    /**
     * 处理数据校验问题
     * @param e
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result handleVaildException(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 Result.fail(errorMap, "数据校验出现问题");
    }

    /**
     * 处理其他异常
     * @param throwable
     * @return
     */
    @ExceptionHandler(value = Throwable.class)
    public Result handleException(Throwable throwable) {
        return Result.fail();
    }
}
    @RequestMapping("/testException")
    public Result testException(@Valid @RequestBody Student student) {
        String name = student.getName();
        Map<String, String> map = new HashMap<>();
        map.put("name", name);
        return Result.ok(map);
    }

测试统一异常处理:测试:http://localhost:8080/testException

{
  "msg": "数据校验出现问题",
  "path": "/test/testException",
  "code": 414,
  "errors": {
    "name": "姓名不能为空"
  }
}

错误信息

{
	"code": 500,
	"msg": "数据校验出现问题",
	"data": {
		"name": "姓名不能为空"
	}
}

分组校验

创建分组校验接口

/**
 * @Author 天才小狐狸
 * @Data 2022/8/11 2:03
 * @Description 姓名校验分组
 */
public interface NameGroup {
}
/**
 * @Author 天才小狐狸
 * @Data 2022/8/11 2:04
 * @Description 年龄校验分组
 */
public interface AgeGroup {
}

添加校验注解

@Data
public class Student {
    @NotEmpty(message ="姓名不能为空",groups =  NameGroup.class)
    private String name;

    @NotEmpty(message ="绰号不能为空",groups = NameGroup.class)
    private  String nickName;

    @Min(value = 18,message = "年龄下限不能低于18岁" ,groups = AgeGroup.class)
    private String age;

    @Max(value = 60,message = "年龄上限不能超过60岁" ,groups = AgeGroup.class)
    private  String retireAge;
}

开启分组校验

@Validated(NameGroup.class)指定校验分组

@RequestMapping("/testGroup")
    public Result testGroup(@Validated(NameGroup.class) @RequestBody Student student) {
        String name = student.getName();
        String nickName = student.getNickName();
        String age = student.getAge();
        String retireAge = student.getRetireAge();
        Map<String, String> map = new HashMap<>();
        map.put("name", name);
        map.put("nickname", nickName);
        map.put("age", age);
        map.put("retireAge", retireAge);
        return Result.ok(map);
    }

测试分组校验:http://localhost:8080/testGroup

{
    "name":"",
    "nickName":"",
    "age":"17",
    "retireAge":"66"
}

错误信息

{
	"code": 500,
	"msg": "数据校验出现问题",
	"data": {
		"nickName": "绰号不能为空",
		"name": "姓名不能为空"
	}
}

@Validated(AgeGroup.class)指定校验分组

@RequestMapping("/testGroup")
    public Result testGroup(@Validated(AgeGroup.class) @RequestBody Student student) {
        String name = student.getName();
        String nickName = student.getNickName();
        String age = student.getAge();
        String retireAge = student.getRetireAge();
        Map<String, String> map = new HashMap<>();
        map.put("name", name);
        map.put("nickname", nickName);
        map.put("age", age);
        map.put("retireAge", retireAge);
        return Result.ok(map);
    }

测试分组校验:http://localhost:8080/testGroup

{
    "name":"",
    "nickName":"",
    "age":"17",
    "retireAge":66
}

错误信息

{
	"code": 500,
	"msg": "数据校验出现问题",
	"data": {
		"retireAge": "年龄上限不能超过60岁",
		"age": "年龄下限不能低于18岁"
	}
}

自定义校验

编写自定义的校验注解

比如要创建一个:@ListValue 注解,被标注的字段值只能是:0或1

@Documented
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
    // 使用该属性去Validation.properties中取
    String message() default "{com.atguigu.common.valid.ListValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    int[] value() default {};
}

设置错误信息:创建文件ValidationMessages.properties

 com.firefly.common.valid.ListValue.message=必须提交指定的值 [0,1]

编写自定义的校验器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

/**
 * @author Michale @EMail:firefly@163.com
 * @Date: 2022/1/8 19:23
 * @Name ListValueConstraintValidator
 * @Description:
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
    private Set<Integer> set = new HashSet<>();

    @Override
    public void initialize(ListValue constraintAnnotation) {
        //获取注解允许的值
        int[] value = constraintAnnotation.value();
        for (int i : value) {
            set.add(i);
        }
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        //判断传入的值是否在满足允许的值
        boolean b = set.contains(value);
        return b;
    }
}

关联校验器和校验注解

在@ListValue注解关联校验器

@Constraint(validatedBy = { ListValueConstraintValidator.class})

一个校验注解可以匹配多个校验器

添加自定义的校验注解

    @ListValue(value = {0,1},groups = {AgeGroup.class,MyJRS303Group.class})
    private Integer gender;

测试自定义校验器:http://localhost:8080/testGroup

{
    "gender":"3"
}
{
	"code": 500,
	"msg": "数据校验出现问题",
	"data": {
		"gender": "必须提交指定的值 [0,1]"
	}
}

以上就是Java利用自定义注解实现数据校验的详细内容,更多关于Java数据校验的资料请关注我们其它相关文章!

(0)

相关推荐

  • java自定义注解实现前后台参数校验的实例

    其实是可以通过@Constraint来限定自定义注解的方法. @Constraint(validatedBy = xxxx.class) 下面是我做的 java自定义注解实现前后台参数校验 的代码示例 对这个感兴趣的,请好好看,好好学: package sonn.sonnannotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.anno

  • Java增加自定义注解进行校验入参详解

    目录 背景 接下来,Show Time 注解类 注解的Aspect类 controller 背景 客户使用我们系统的时候,查询不带任何查询条件,查询就返回全部数据,500多万条数据啊,然后直接导出,数据量庞大,接口超时,这可苦了我们这些开发人员,一边优化,一边挨喷.这么多数据就算导成功了,Excel也打不开呀.迫不得已,决定强制让客户至少传入一个参数进行查询来缓解服务器以及开发人员的压力. 首先想到的,最简单的,就是增加一个静态方法,在每个方法入口调一下,来校验以及抛出错误.但是转念一想,更优美

  • Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)

    目录 @Valid和@Validated @Valid和@Validated比较 @Valid高级使用 @Valid级联校验 @Validated高级使用 @Validated分组校验 @Validated分组校验顺序 @Validated非实体类校验 @PathVariable 正则表达式校验 继承BasicErrorController类 自定义校验注解 @Valid和@Validated @Valid和@Validated比较 相同点: @Valid注解和 @Validated注解都是开启

  • Java利用自定义注解、反射实现简单BaseDao实例

    在常见的ORM框架中,大都提供了使用注解方式来实现entity与数据库的映射,这里简单地使用自定义注解与反射来生成可执行的sql语句. 这是整体的目录结构,本来是为复习注解建立的项目^.^ 好的,首先我们来确定思路. 1. 自定义@Table @Column注解, 我们稍微模仿hibernate,让@Table作用于类上,来表明实体类与数据表的映射关系,且让@Table中的属性value映射为数据表的名称tableName:让@Column作用于属性上(这里没实现作用于set方法上),表明属性与

  • SpringBoot自定义注解实现Token校验的方法

    1.定义Token的注解,需要Token校验的接口,方法上加上此注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementTyp

  • SpringBoot通过自定义注解实现参数校验

    目录 1. 为什么要进行参数校验 2. 如何实现参数校验 3. 注解实现参数校验 4. 自定义注解实现参数校验 1. 为什么要进行参数校验 在后端进行工作时,需要接收前端传来的数据去数据库查询,但是如果有些数据过于离谱,我们就可以直接把它pass掉,不让这种垃圾数据接触数据库,减小数据库的压力. 有时候会有不安分的人通过一些垃圾数据攻击咱们的程序,让咱们的服务器或数据库崩溃,这种攻击虽然低级但不得不防,就像QQ进行登录请求时,它们向后端发送 账号=123,密码=123 的数据,一秒钟还发1w次,

  • 详解Java中自定义注解的使用

    目录 什么是注解 注解的注意事项 注解的本质 自定义注解使用 使用方式 1 使用方式 2 什么是注解 在早期的工作的时候 ,自定义注解写的比较多,可大多都只是因为 这样看起来 不会存在一堆代码耦合在一起的情况,所以使用了自定义注解,这样看起来清晰些, Annontation是Java5开始引入的新特征,中文名称叫注解. 它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类.方法.成员变量等)进行关联.为程序的元素(类.方法.成员变量)加上更直观.更明了的说

  • Java中自定义注解介绍与使用场景详解

    注解的概念及分类 1.首先我们来看一下什么是注解: 注解就是某种注解类型的一个实例,我们可以用它在某个类上进行标注,这样编译器在编译我们的文件时,会根据我们自己设定的方法来编译类. 2.注解的分类 注解大体上分为三种:标记注解,一般注解,元注解,@Override用于标识,该方法是继承自超类的.这样,当超类的方法修改后,实现类就可以直接看到了.而@Deprecated注解,则是标识当前方法或者类已经不推荐使用,如果用户还是要使用,会生成编译的警告. 本文主要介绍的是关于Java自定义注解,下面话

  • 谈谈Java中自定义注解及使用场景

    Java自定义注解一般使用场景为:自定义注解+拦截器或者AOP,使用自定义注解来自己设计框架,使得代码看起来非常优雅.本文将先从自定义注解的基础概念说起,然后开始实战,写小段代码实现自定义注解+拦截器,自定义注解+AOP. 一. 什么是注解(Annotation) Java注解是什么,以下是引用自维基百科的内容 Java注解又称Java标注,是JDK5.0版本开始支持加入源代码的特殊语法元数据. Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java标注可以通过反

  • Spring boot通过切面,实现超灵活的注解式数据校验过程

    目录 通过切面,实现超灵活的注解式数据校验 Spring MVC的校验方式 通过切面实现自己的注解式数据校验 Spring boot aop注解数据权限校验 注解类 AOP切面 使用 通过切面,实现超灵活的注解式数据校验 在企业系统的开发中,用户表单输入的场景是会经常遇见的,如何让数据校验脱离于业务代码逻辑,谁也不想在逻辑代码里对字段逐一判断.... Spring MVC的校验方式 在使用Spring MVC时的时候,直接使用hibernate-validator的注解,如下: public c

  • Feign利用自定义注解实现路径转义详解

    目录 背景 解决方案 最后 背景 近期由于项目中需要,所以需要通过Feign封装一个对Harbor操作的sdk信息. 在调用的过程中发现,当请求参数中带有"/"时,Feign默认会将"/"当成路径去解析,而不是当成完整的一个参数解析,实例如下 请求路径为:api/v2.0/projects/{projectName}/repositories 注解参数为:@PathVariable("projectName") 正常请求为:api/v2.0/pr

  • SpringBoot使用自定义注解实现数据脱敏过程详细解析

    目录 前言 一.引入hutool工具类 二.定义常用需要脱敏的数据类型的枚举 三.定义脱敏方式枚举 四.自定义脱敏的注解 五.自定义Jackson的序列化方式 六.使用 七.脱敏效果 前言 对于某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作,例如银行卡号.身份证号.手机号等,脱敏方式有多种方式.可以修改SQL语句,也可以写硬代码,也可以修改JSON序列化,这里介绍通过修改Jackson序列化方式实现数据脱敏. 一.引入hutool工具类 maven: <dependency> <g

  • Java使用自定义注解实现为事件源绑定事件监听器操作示例

    本文实例讲述了Java使用自定义注解实现为事件源绑定事件监听器操作.分享给大家供大家参考,具体如下: 一 定义注解 import java.lang.annotation.*; import java.awt.event.*; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ActionListenerFor { // 定义一个成员变量,用于设置元数据 // 该listener成员变

随机推荐