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

作者:扫地和尚 时间:2022-12-03 09:56:49 

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类型上,因为表单值为&ldquo;&rdquo;时无法转换为int,但可以转换为Stirng为&rdquo;&ldquo;,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=&rdquo;range.bean.wage&rdquo;) 

  • @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]"
}
}

 

来源:https://juejin.cn/post/7138373378070560782

标签:Java,注解,数据,校验
0
投稿

猜你喜欢

  • java线程间通信的通俗解释及代码示例

    2023-12-06 18:04:43
  • 操作xml,将xml数据显示到treeview的C#代码

    2023-01-02 19:56:48
  • springBoot详解集成Swagger流程

    2023-02-26 22:23:33
  • 详解Spring boot/Spring 统一错误处理方案的使用

    2023-11-24 12:56:07
  • SpringBoot小程序推送信息的项目实践

    2021-12-07 04:23:34
  • 心动吗?正大光明的免费使用IntelliJ IDEA商业版

    2023-11-25 04:29:48
  • Java文件读写IO/NIO及性能比较详细代码及总结

    2021-11-28 12:56:30
  • JAVA List和Map切割工具详解

    2023-01-27 11:32:42
  • 详解spring中的Aware接口功能

    2023-07-02 00:36:01
  • MyBatis的注解使用、ORM层优化方式(懒加载和缓存)

    2023-04-24 09:16:13
  • C#实现Dictionary字典赋值的方法

    2021-12-09 17:55:40
  • springboot读取配置文件中的参数具体步骤

    2023-11-29 05:46:14
  • 基于spring @Cacheable 注解的spel表达式解析执行逻辑

    2023-07-03 19:46:45
  • 浅谈collection标签的oftype属性能否为java.util.Map

    2023-03-19 23:16:15
  • Android编程实现使用webView打开本地html文件的方法

    2023-04-26 14:30:38
  • 深入理解Javascript中的this关键字

    2022-03-08 16:52:25
  • Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

    2022-05-29 09:46:46
  • c#反射表达式树模糊搜索示例

    2022-01-09 07:03:31
  • 深入理解Kotlin的泛型系统

    2023-09-09 00:47:30
  • JAVA利用递归删除文件代码实例

    2022-12-10 23:59:53
  • asp之家 软件编程 m.aspxhome.com